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_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"; | ||||
| 
 | ||||
|  |  | |||
|  | @ -172,9 +172,10 @@ void ApiWrap::savePinnedOrder() { | |||
| 
 | ||||
| void ApiWrap::toggleChannelGrouping( | ||||
| 		not_null<ChannelData*> channel, | ||||
| 		bool group) { | ||||
| 		bool group, | ||||
| 		base::lambda<void()> callback) { | ||||
| 	if (const auto already = _channelGroupingRequests.take(channel)) { | ||||
| 		request(*already).cancel(); | ||||
| 		request(already->first).cancel(); | ||||
| 	} | ||||
| 	const auto feedId = Data::Feed::kId; | ||||
| 	const auto flags = group | ||||
|  | @ -185,15 +186,18 @@ void ApiWrap::toggleChannelGrouping( | |||
| 		channel->inputChannel, | ||||
| 		MTP_int(feedId) | ||||
| 	)).done([=](const MTPBool &result) { | ||||
| 		_channelGroupingRequests.remove(channel); | ||||
| 		if (group) { | ||||
| 			channel->setFeed(Auth().data().feed(feedId)); | ||||
| 		} else { | ||||
| 			channel->clearFeed(); | ||||
| 		} | ||||
| 		if (const auto data = _channelGroupingRequests.take(channel)) { | ||||
| 			data->second(); | ||||
| 		} | ||||
| 	}).fail([=](const RPCError &error) { | ||||
| 		_channelGroupingRequests.remove(channel); | ||||
| 	}).send(); | ||||
| 	_channelGroupingRequests.emplace(channel, requestId, callback); | ||||
| } | ||||
| 
 | ||||
| void ApiWrap::sendMessageFail(const RPCError &error) { | ||||
|  | @ -362,6 +366,124 @@ void ApiWrap::requestContacts() { | |||
| 	}).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) { | ||||
| 	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 hto = App::historyLoaded(channel->id)) { | ||||
| 						if (!h->isEmpty()) { | ||||
| 							h->clear(true); | ||||
| 							h->unloadBlocks(); | ||||
| 						} | ||||
| 						if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) { | ||||
| 							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->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()); | ||||
| 		if (auto h = App::historyLoaded(channel->id)) { | ||||
| 			if (h->inboxReadBefore < f.vread_inbox_max_id.v + 1) { | ||||
| 				h->setUnreadCount(f.vunread_count.v); | ||||
| 				h->inboxReadBefore = f.vread_inbox_max_id.v + 1; | ||||
| 		if (const auto history = App::historyLoaded(channel->id)) { | ||||
| 			history->applyDialogFields( | ||||
| 				f.vunread_count.v, | ||||
| 				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()) { | ||||
| 			channel->setPinnedMessageId(f.vpinned_msg_id.v); | ||||
| 		} else { | ||||
|  | @ -1063,7 +1183,9 @@ void ApiWrap::unblockParticipant( | |||
| 		not_null<ChannelData*> channel, | ||||
| 		not_null<UserData*> user) { | ||||
| 	const auto kick = KickRequest(channel, user); | ||||
| 	if (_kickRequests.contains(kick)) return; | ||||
| 	if (_kickRequests.contains(kick)) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	const auto requestId = request(MTPchannels_EditBanned( | ||||
| 		channel->inputChannel, | ||||
|  | @ -1085,6 +1207,43 @@ void ApiWrap::unblockParticipant( | |||
| 	_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( | ||||
| 		not_null<ChannelData*> channel, | ||||
| 		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) { | ||||
| 	auto lastMsgId = MsgId(0); | ||||
| 	auto deleteTillId = MsgId(0); | ||||
| 	if (auto history = App::historyLoaded(peer->id)) { | ||||
| 		if (history->lastMsg) { | ||||
| 			lastMsgId = history->lastMsg->id; | ||||
| 			Local::addSavedPeer(history->peer, history->lastMsg->date); | ||||
| 		if (const auto last = history->lastMessage()) { | ||||
| 			deleteTillId = last->id; | ||||
| 			Local::addSavedPeer(history->peer, last->date); | ||||
| 		} | ||||
| 		history->clear(); | ||||
| 		history->newLoaded = history->oldLoaded = true; | ||||
| 	} | ||||
| 	if (auto channel = peer->asChannel()) { | ||||
| 		if (auto migrated = peer->migrateFrom()) { | ||||
| 	if (const auto channel = peer->asChannel()) { | ||||
| 		if (const auto migrated = peer->migrateFrom()) { | ||||
| 			clearHistory(migrated); | ||||
| 		} | ||||
| 		if (IsServerMsgId(lastMsgId)) { | ||||
| 		if (IsServerMsgId(deleteTillId)) { | ||||
| 			request(MTPchannels_DeleteHistory( | ||||
| 				channel->inputChannel, | ||||
| 				MTP_int(lastMsgId) | ||||
| 				MTP_int(deleteTillId) | ||||
| 			)).send(); | ||||
| 		} | ||||
| 	} else { | ||||
|  | @ -1530,7 +1689,7 @@ void ApiWrap::clearHistory(not_null<PeerData*> peer) { | |||
| 			peer->input, | ||||
| 			MTP_int(0) | ||||
| 		)).done([=](const MTPmessages_AffectedHistory &result) { | ||||
| 			auto offset = applyAffectedHistory(peer, result); | ||||
| 			const auto offset = applyAffectedHistory(peer, result); | ||||
| 			if (offset > 0) { | ||||
| 				clearHistory(peer); | ||||
| 			} | ||||
|  | @ -1775,6 +1934,94 @@ void ApiWrap::requestParticipantsCountDelayed( | |||
| 		[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) { | ||||
| 	const QVector<MTPMessage> *v = 0; | ||||
| 	switch (msgs.type()) { | ||||
|  | @ -3438,7 +3685,11 @@ void ApiWrap::readServerHistory(not_null<History*> history) { | |||
| 
 | ||||
| void ApiWrap::readServerHistoryForce(not_null<History*> history) { | ||||
| 	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 (!channel->amIn()) { | ||||
| 			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 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)>; | ||||
| 	void requestMessageData( | ||||
|  | @ -65,6 +68,8 @@ public: | |||
| 		RequestMessageDataCallback callback); | ||||
| 
 | ||||
| 	void requestContacts(); | ||||
| 	void requestDialogEntry(not_null<Data::Feed*> feed); | ||||
| 	void requestDialogEntry(not_null<History*> history); | ||||
| 
 | ||||
| 	void requestFullPeer(PeerData *peer); | ||||
| 	void requestPeer(PeerData *peer); | ||||
|  | @ -73,6 +78,7 @@ public: | |||
| 	void requestBots(not_null<ChannelData*> channel); | ||||
| 	void requestAdmins(not_null<ChannelData*> channel); | ||||
| 	void requestParticipantsCountDelayed(not_null<ChannelData*> channel); | ||||
| 	void requestChannelRangeDifference(not_null<History*> history); | ||||
| 
 | ||||
| 	void requestChangelog( | ||||
| 		const QString &sinceVersion, | ||||
|  | @ -96,6 +102,9 @@ public: | |||
| 	void unblockParticipant( | ||||
| 		not_null<ChannelData*> channel, | ||||
| 		not_null<UserData*> user); | ||||
| 	void deleteAllFromUser( | ||||
| 		not_null<ChannelData*> channel, | ||||
| 		not_null<UserData*> from); | ||||
| 
 | ||||
| 	void requestWebPageDelayed(WebPageData *page); | ||||
| 	void clearWebPageRequest(WebPageData *page); | ||||
|  | @ -269,6 +278,7 @@ private: | |||
| 
 | ||||
| 	QVector<MTPint> collectMessageIds(const MessageDataRequests &requests); | ||||
| 	MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false); | ||||
| 	void applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs); | ||||
| 
 | ||||
| 	void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req); | ||||
| 	void gotUserFull(UserData *user, const MTPUserFull &result, mtpRequestId req); | ||||
|  | @ -285,10 +295,24 @@ private: | |||
| 		int availableCount, | ||||
| 		const QVector<MTPChannelParticipant> &list); | ||||
| 	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); | ||||
| 
 | ||||
| 	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 stickersSaveOrder(); | ||||
|  | @ -347,6 +371,11 @@ private: | |||
| 	void applyAffectedMessages( | ||||
| 		not_null<PeerData*> peer, | ||||
| 		const MTPmessages_AffectedMessages &result); | ||||
| 
 | ||||
| 	void deleteAllFromUserSend( | ||||
| 		not_null<ChannelData*> channel, | ||||
| 		not_null<UserData*> from); | ||||
| 
 | ||||
| 	void sendMessageFail(const RPCError &error); | ||||
| 	void uploadAlbumMedia( | ||||
| 		not_null<HistoryItem*> item, | ||||
|  | @ -391,7 +420,7 @@ private: | |||
| 		const MTPchannels_ChannelParticipants&)> _channelMembersForAddCallback; | ||||
| 	base::flat_map< | ||||
| 		not_null<ChannelData*>, | ||||
| 		mtpRequestId> _channelGroupingRequests; | ||||
| 		std::pair<mtpRequestId,base::lambda<void()>>> _channelGroupingRequests; | ||||
| 
 | ||||
| 	using KickRequest = std::pair< | ||||
| 		not_null<ChannelData*>, | ||||
|  | @ -400,6 +429,10 @@ private: | |||
| 
 | ||||
| 	QMap<ChannelData*, mtpRequestId> _selfParticipantRequests; | ||||
| 
 | ||||
| 	base::flat_map< | ||||
| 		not_null<ChannelData*>, | ||||
| 		mtpRequestId> _rangeDifferenceRequests; | ||||
| 
 | ||||
| 	QMap<WebPageData*, mtpRequestId> _webPagesPending; | ||||
| 	base::Timer _webPagesTimer; | ||||
| 
 | ||||
|  | @ -432,6 +465,8 @@ private: | |||
| 
 | ||||
| 	mtpRequestId _contactsRequestId = 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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -459,7 +459,7 @@ namespace { | |||
| 					if (auto h = App::historyLoaded(cdata->id)) { | ||||
| 						if (auto hto = App::historyLoaded(channel->id)) { | ||||
| 							if (!h->isEmpty()) { | ||||
| 								h->clear(true); | ||||
| 								h->unloadBlocks(); | ||||
| 							} | ||||
| 							if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) { | ||||
| 								App::main()->removeDialog(h); | ||||
|  | @ -1019,7 +1019,7 @@ namespace { | |||
| 	} | ||||
| 
 | ||||
| 	void feedInboxRead(const PeerId &peer, MsgId upTo) { | ||||
| 		if (auto history = App::historyLoaded(peer)) { | ||||
| 		if (const auto history = App::historyLoaded(peer)) { | ||||
| 			history->inboxRead(upTo); | ||||
| 		} | ||||
| 	} | ||||
|  | @ -1027,17 +1027,8 @@ namespace { | |||
| 	void feedOutboxRead(const PeerId &peer, MsgId upTo, TimeId when) { | ||||
| 		if (auto history = App::historyLoaded(peer)) { | ||||
| 			history->outboxRead(upTo); | ||||
| 			if (history->lastMsg | ||||
| 				&& history->lastMsg->out() | ||||
| 				&& 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); | ||||
| 			if (const auto user = history->peer->asUser()) { | ||||
| 				user->madeAction(when); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | @ -1055,37 +1046,31 @@ namespace { | |||
| 		return &(*i); | ||||
| 	} | ||||
| 
 | ||||
| 	void feedWereDeleted(ChannelId channelId, const QVector<MTPint> &msgsIds) { | ||||
| 	void feedWereDeleted( | ||||
| 			ChannelId channelId, | ||||
| 			const QVector<MTPint> &msgsIds) { | ||||
| 		const auto data = fetchMsgsData(channelId, false); | ||||
| 		if (!data) return; | ||||
| 
 | ||||
| 		const auto channelHistory = (channelId != NoChannel) | ||||
| 			? App::history(peerFromChannel(channelId))->asChannelHistory() | ||||
| 		const auto affectedHistory = (channelId != NoChannel) | ||||
| 			? App::history(peerFromChannel(channelId)).get() | ||||
| 			: nullptr; | ||||
| 
 | ||||
| 		base::flat_set<not_null<History*>> historiesToCheck; | ||||
| 		auto historiesToCheck = base::flat_set<not_null<History*>>(); | ||||
| 		for (const auto msgId : msgsIds) { | ||||
| 			auto j = data->constFind(msgId.v); | ||||
| 			if (j != data->cend()) { | ||||
| 				const auto h = (*j)->history(); | ||||
| 				const auto history = (*j)->history(); | ||||
| 				(*j)->destroy(); | ||||
| 				if (!h->lastMsg) { | ||||
| 					historiesToCheck.emplace(h); | ||||
| 				if (!history->lastMessageKnown()) { | ||||
| 					historiesToCheck.emplace(history); | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (channelHistory) { | ||||
| 					if (channelHistory->unreadCount() > 0 | ||||
| 						&& msgId.v >= channelHistory->inboxReadBefore) { | ||||
| 						channelHistory->setUnreadCount( | ||||
| 							channelHistory->unreadCount() - 1); | ||||
| 			} else if (affectedHistory) { | ||||
| 				affectedHistory->unknownMessageDeleted(msgId.v); | ||||
| 			} | ||||
| 		} | ||||
| 			} | ||||
| 		} | ||||
| 		if (main()) { | ||||
| 		for (const auto history : historiesToCheck) { | ||||
| 				main()->checkPeerHistory(history->peer); | ||||
| 			} | ||||
| 			Auth().api().requestDialogEntry(history); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1222,10 +1207,6 @@ namespace { | |||
| 		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) { | ||||
| 		if (!peer) { | ||||
| 			return nullptr; | ||||
|  |  | |||
|  | @ -148,7 +148,6 @@ namespace App { | |||
| 
 | ||||
| 	Histories &histories(); | ||||
| 	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); | ||||
| 	HistoryItem *histItemById(ChannelId channelId, MsgId itemId); | ||||
| 	inline not_null<History*> history(const PeerData *peer) { | ||||
|  |  | |||
|  | @ -568,7 +568,7 @@ void DeleteMessagesBox::deleteAndClear() { | |||
| 					MTP_vector<MTPint>(1, MTP_int(_ids[0].msg)))); | ||||
| 		} | ||||
| 		if (_deleteAll && _deleteAll->checked()) { | ||||
| 			App::main()->deleteAllFromUser( | ||||
| 			Auth().api().deleteAllFromUser( | ||||
| 				_moderateInChannel, | ||||
| 				_moderateFrom); | ||||
| 		} | ||||
|  | @ -583,13 +583,13 @@ void DeleteMessagesBox::deleteAndClear() { | |||
| 		if (auto item = App::histItemById(itemId)) { | ||||
| 			auto history = item->history(); | ||||
| 			auto wasOnServer = (item->id > 0); | ||||
| 			auto wasLast = (history->lastMsg == item); | ||||
| 			auto wasLast = (history->lastMessage() == item); | ||||
| 			item->destroy(); | ||||
| 
 | ||||
| 			if (wasOnServer) { | ||||
| 				idsByPeer[history->peer].push_back(MTP_int(itemId.msg)); | ||||
| 			} 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) { | ||||
| 	Auth().api().requestDialogEntry(App::history(row->peer())); | ||||
| 	Ui::showPeerHistory(row->peer(), ShowAtUnreadMsgId); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -72,8 +72,10 @@ void Feed::registerOne(not_null<ChannelData*> channel) { | |||
| 	if (!base::contains(_channels, history)) { | ||||
| 		const auto invisible = (_channels.size() < 2); | ||||
| 		_channels.push_back(history); | ||||
| 		if (history->lastMsg) { | ||||
| 			updateLastMessage(history->lastMsg); | ||||
| 		if (history->lastMessageKnown()) { | ||||
| 			recountLastMessage(); | ||||
| 		} else if (_channelsLoaded) { | ||||
| 			_parent->session().api().requestDialogEntry(history); | ||||
| 		} | ||||
| 		_parent->session().storage().remove( | ||||
| 			Storage::FeedMessagesInvalidate(_id)); | ||||
|  | @ -96,8 +98,10 @@ void Feed::unregisterOne(not_null<ChannelData*> channel) { | |||
| 	if (i != end(_channels)) { | ||||
| 		const auto visible = (_channels.size() > 1); | ||||
| 		_channels.erase(i, end(_channels)); | ||||
| 		if (_lastMessage && _lastMessage->history() == history) { | ||||
| 			messageRemoved(_lastMessage); | ||||
| 		if (const auto last = lastMessage()) { | ||||
| 			if (last->history() == history) { | ||||
| 				recountLastMessage(); | ||||
| 			} | ||||
| 		} | ||||
| 		_parent->session().storage().remove( | ||||
| 			Storage::FeedMessagesRemoveAll(_id, channel->bareId())); | ||||
|  | @ -115,8 +119,10 @@ void Feed::unregisterOne(not_null<ChannelData*> channel) { | |||
| } | ||||
| 
 | ||||
| void Feed::updateLastMessage(not_null<HistoryItem*> item) { | ||||
| 	if (justSetLastMessage(item)) { | ||||
| 		setChatsListDate(_lastMessage->date); | ||||
| 	if (justUpdateLastMessage(item)) { | ||||
| 		if (_lastMessage && *_lastMessage) { | ||||
| 			setChatsListDate((*_lastMessage)->date); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -207,8 +213,11 @@ void Feed::setChannels(std::vector<not_null<ChannelData*>> channels) { | |||
| 	_parent->notifyFeedUpdated(this, FeedUpdateFlag::Channels); | ||||
| } | ||||
| 
 | ||||
| bool Feed::justSetLastMessage(not_null<HistoryItem*> item) { | ||||
| 	if (_lastMessage && item->position() <= _lastMessage->position()) { | ||||
| bool Feed::justUpdateLastMessage(not_null<HistoryItem*> item) { | ||||
| 	if (!_lastMessage) { | ||||
| 		return false; | ||||
| 	} else if (*_lastMessage | ||||
| 		&& item->position() <= (*_lastMessage)->position()) { | ||||
| 		return false; | ||||
| 	} | ||||
| 	_lastMessage = item; | ||||
|  | @ -216,37 +225,107 @@ bool Feed::justSetLastMessage(not_null<HistoryItem*> item) { | |||
| } | ||||
| 
 | ||||
| void Feed::messageRemoved(not_null<HistoryItem*> item) { | ||||
| 	if (_lastMessage == item) { | ||||
| 	if (lastMessage() == item) { | ||||
| 		_lastMessage = base::none; | ||||
| 		recountLastMessage(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Feed::historyCleared(not_null<History*> history) { | ||||
| 	if (_lastMessage->history() == history) { | ||||
| 		recountLastMessage(); | ||||
| 	if (const auto last = lastMessage()) { | ||||
| 		if (last->history() == history) { | ||||
| 			messageRemoved(last); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Feed::recountLastMessage() { | ||||
| 	for (const auto history : _channels) { | ||||
| 		if (!history->lastMessageKnown()) { | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	_lastMessage = nullptr; | ||||
| 	for (const auto history : _channels) { | ||||
| 		if (const auto last = history->lastMsg) { | ||||
| 			justSetLastMessage(last); | ||||
| 		if (const auto last = history->lastMessage()) { | ||||
| 			justUpdateLastMessage(last); | ||||
| 		} | ||||
| 	} | ||||
| 	if (_lastMessage) { | ||||
| 		setChatsListDate(_lastMessage->date); | ||||
| 	updateChatsListDate(); | ||||
| } | ||||
| 
 | ||||
| void Feed::updateChatsListDate() { | ||||
| 	if (_lastMessage && *_lastMessage) { | ||||
| 		setChatsListDate((*_lastMessage)->date); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Feed::setUnreadCounts(int unreadCount, int unreadMutedCount) { | ||||
| 	_unreadCount = unreadCount; | ||||
| HistoryItem *Feed::lastMessage() const { | ||||
| 	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; | ||||
| } | ||||
| 
 | ||||
| void Feed::setUnreadPosition(const MessagePosition &position) { | ||||
| 	if (_unreadPosition.current() < 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 { | ||||
| 	return _unreadPosition.current(); | ||||
|  | @ -265,15 +344,15 @@ bool Feed::shouldBeInChatList() const { | |||
| } | ||||
| 
 | ||||
| int Feed::chatListUnreadCount() const { | ||||
| 	return _unreadCount; | ||||
| 	return _unreadCount ? *_unreadCount : 0; | ||||
| } | ||||
| 
 | ||||
| bool Feed::chatListMutedBadge() const { | ||||
| 	return _unreadCount <= _unreadMutedCount; | ||||
| 	return _unreadCount ? (*_unreadCount <= _unreadMutedCount) : false; | ||||
| } | ||||
| 
 | ||||
| HistoryItem *Feed::chatsListItem() const { | ||||
| 	return _lastMessage; | ||||
| 	return lastMessage(); | ||||
| } | ||||
| 
 | ||||
| const QString &Feed::chatsListName() const { | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position); | |||
| class Feed : public Dialogs::Entry { | ||||
| public: | ||||
| 	static constexpr auto kId = 1; | ||||
| 	static constexpr auto kChannelsLimit = 1000; | ||||
| 
 | ||||
| 	Feed(FeedId id, not_null<Data::Session*> parent); | ||||
| 
 | ||||
|  | @ -43,11 +44,18 @@ public: | |||
| 	void messageRemoved(not_null<HistoryItem*> item); | ||||
| 	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 unreadCountChanged( | ||||
| 		base::optional<int> unreadCountDelta, | ||||
| 		int mutedCountDelta); | ||||
| 	MessagePosition unreadPosition() const; | ||||
| 	rpl::producer<MessagePosition> unreadPositionChanges() const; | ||||
| 
 | ||||
| 	HistoryItem *lastMessage() const; | ||||
| 	bool lastMessageKnown() const; | ||||
| 
 | ||||
| 	bool toImportant() const override; | ||||
| 	bool shouldBeInChatList() const override; | ||||
| 	int chatListUnreadCount() const override; | ||||
|  | @ -73,7 +81,8 @@ public: | |||
| private: | ||||
| 	void indexNameParts(); | ||||
| 	void recountLastMessage(); | ||||
| 	bool justSetLastMessage(not_null<HistoryItem*> item); | ||||
| 	bool justUpdateLastMessage(not_null<HistoryItem*> item); | ||||
| 	void updateChatsListDate(); | ||||
| 
 | ||||
| 	FeedId _id = 0; | ||||
| 	not_null<Data::Session*> _parent; | ||||
|  | @ -83,10 +92,10 @@ private: | |||
| 	QString _name; | ||||
| 	base::flat_set<QString> _nameWords; | ||||
| 	base::flat_set<QChar> _nameFirstLetters; | ||||
| 	HistoryItem *_lastMessage = nullptr; | ||||
| 	base::optional<HistoryItem*> _lastMessage; | ||||
| 
 | ||||
| 	rpl::variable<MessagePosition> _unreadPosition; | ||||
| 	int _unreadCount = 0; | ||||
| 	base::optional<int> _unreadCount; | ||||
| 	int _unreadMutedCount = 0; | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -849,13 +849,22 @@ void ChannelData::setAvailableMinId(MsgId availableMinId) { | |||
| 		if (auto history = App::historyLoaded(this)) { | ||||
| 			history->clearUpTill(availableMinId); | ||||
| 		} | ||||
| 		if (_pinnedMessageId <= _availableMinId) { | ||||
| 			_pinnedMessageId = MsgId(0); | ||||
| 			Notify::peerUpdatedDelayed( | ||||
| 				this, | ||||
| 				Notify::PeerUpdate::Flag::ChannelPinnedChanged); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ChannelData::setPinnedMessageId(MsgId messageId) { | ||||
| 	messageId = (messageId > _availableMinId) ? messageId : MsgId(0); | ||||
| 	if (_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; | ||||
| 	} | ||||
| 
 | ||||
| 	const auto history = App::historyFromDialog( | ||||
| 		peerId, | ||||
| 		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); | ||||
| 	const auto history = App::history(peerId); | ||||
| 	history->applyDialog(dialog); | ||||
| 
 | ||||
| 	if (!history->isPinnedDialog() && !history->chatsListDate().isNull()) { | ||||
| 		addSavedPeersAfter(history->chatsListDate()); | ||||
| 	} | ||||
| 	_contactsNoDialogs->del(history); | ||||
| 	if (peer->migrateFrom()) { | ||||
| 		if (const auto historyFrom = App::historyLoaded(peer->migrateFrom())) { | ||||
| 	if (const auto from = history->peer->migrateFrom()) { | ||||
| 		if (const auto historyFrom = App::historyLoaded(from)) { | ||||
| 			removeDialog(historyFrom); | ||||
| 		} | ||||
| 	} else if (peer->migrateTo() && peer->migrateTo()->amIn()) { | ||||
| 	} else if (const auto to = history->peer->migrateTo()) { | ||||
| 		if (to->amIn()) { | ||||
| 			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) { | ||||
| 	const auto peerId = peerFromMTP(dialog.vpeer); | ||||
| 	const auto feedId = dialog.vfeed_id.v; | ||||
| 	const auto feed = Auth().data().feed(feedId); | ||||
| 	const auto addChannel = [&](ChannelId channelId) { | ||||
| 		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); | ||||
| 	feed->applyDialog(dialog); | ||||
| 
 | ||||
| 	if (!feed->isPinnedDialog() && !feed->chatsListDate().isNull()) { | ||||
| 		addSavedPeersAfter(feed->chatsListDate()); | ||||
| 	} | ||||
| 	if (dialog.has_read_max_position()) { | ||||
| 		feed->setUnreadPosition( | ||||
| 			Data::FeedPositionFromMTP(dialog.vread_max_position)); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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" | ||||
| 
 | ||||
| class History; | ||||
| class ChannelHistory; | ||||
| class HistoryBlock; | ||||
| class HistoryItem; | ||||
| class HistoryMessage; | ||||
|  | @ -50,12 +49,7 @@ enum NewMessageType : char { | |||
| 
 | ||||
| class Histories { | ||||
| public: | ||||
| 	using Map = QHash<PeerId, History*>; | ||||
| 	Map map; | ||||
| 
 | ||||
| 	Histories() : _a_typings(animation(this, &Histories::step_typings)) { | ||||
| 		_selfDestructTimer.setCallback([this] { checkSelfDestructItems(); }); | ||||
| 	} | ||||
| 	Histories(); | ||||
| 
 | ||||
| 	void registerSendAction( | ||||
| 		not_null<History*> history, | ||||
|  | @ -64,20 +58,16 @@ public: | |||
| 		TimeId when); | ||||
| 	void step_typings(TimeMs ms, bool timer); | ||||
| 
 | ||||
| 	History *find(const PeerId &peerId); | ||||
| 	not_null<History*> findOrInsert(const PeerId &peerId); | ||||
| 	not_null<History*> findOrInsert( | ||||
| 		const PeerId &peerId, | ||||
| 		int unreadCount, | ||||
| 		MsgId maxInboxRead, | ||||
| 		MsgId maxOutboxRead); | ||||
| 	History *find(PeerId peerId) const; | ||||
| 	not_null<History*> findOrInsert(PeerId peerId); | ||||
| 
 | ||||
| 	void clear(); | ||||
| 	void remove(const PeerId &peer); | ||||
| 
 | ||||
| 	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; | ||||
| 	BasicAnimation _a_typings; | ||||
| 
 | ||||
|  | @ -114,6 +104,8 @@ public: | |||
| private: | ||||
| 	void checkSelfDestructItems(); | ||||
| 
 | ||||
| 	std::unordered_map<PeerId, std::unique_ptr<History>> _map; | ||||
| 
 | ||||
| 	int _unreadFull = 0; | ||||
| 	int _unreadMuted = 0; | ||||
| 	base::Observable<SendActionAnimationUpdate> _sendActionAnimationUpdated; | ||||
|  | @ -136,29 +128,24 @@ public: | |||
| 	History(const History &) = delete; | ||||
| 	History &operator=(const History &) = delete; | ||||
| 
 | ||||
| 	ChannelId channelId() const { | ||||
| 		return peerToChannel(peer->id); | ||||
| 	} | ||||
| 	bool isChannel() const { | ||||
| 		return peerIsChannel(peer->id); | ||||
| 	} | ||||
| 	bool isMegagroup() const { | ||||
| 		return peer->isMegagroup(); | ||||
| 	} | ||||
| 	ChannelHistory *asChannelHistory(); | ||||
| 	const ChannelHistory *asChannelHistory() const; | ||||
| 
 | ||||
| 	ChannelId channelId() const; | ||||
| 	bool isChannel() const; | ||||
| 	bool isMegagroup() const; | ||||
| 	not_null<History*> migrateToOrMe() const; | ||||
| 	History *migrateFrom() const; | ||||
| 	MsgRange rangeForDifferenceRequest() const; | ||||
| 	HistoryService *insertJoinedMessage(bool unread); | ||||
| 	void checkJoinedMessage(bool createUnread = false); | ||||
| 
 | ||||
| 	bool isEmpty() const { | ||||
| 		return blocks.empty(); | ||||
| 	} | ||||
| 	bool isEmpty() const; | ||||
| 	bool isDisplayedEmpty() const; | ||||
| 	bool hasOrphanMediaGroupPart() const; | ||||
| 	bool removeOrphanMediaGroupPart(); | ||||
| 	QVector<MsgId> collectMessagesFromUserToDelete( | ||||
| 		not_null<UserData*> user) const; | ||||
| 
 | ||||
| 	void clear(bool leaveItems = false); | ||||
| 	void clear(); | ||||
| 	void unloadBlocks(); | ||||
| 	void clearUpTill(MsgId availableMinId); | ||||
| 
 | ||||
| 	void applyGroupAdminChanges(const base::flat_map<UserId, bool> &changes); | ||||
|  | @ -184,18 +171,17 @@ public: | |||
| 	void newItemAdded(not_null<HistoryItem*> item); | ||||
| 
 | ||||
| 	int countUnread(MsgId upTo); | ||||
| 	MsgId inboxRead(MsgId upTo); | ||||
| 	MsgId inboxRead(HistoryItem *wasRead); | ||||
| 	MsgId outboxRead(MsgId upTo); | ||||
| 	MsgId outboxRead(HistoryItem *wasRead); | ||||
| 	MsgId readInbox(); | ||||
| 	void inboxRead(MsgId upTo); | ||||
| 	void inboxRead(not_null<const 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 { | ||||
| 		return _unreadCount; | ||||
| 	} | ||||
| 	int unreadCount() const; | ||||
| 	void setUnreadCount(int newUnreadCount); | ||||
| 	bool mute() const { | ||||
| 		return _mute; | ||||
| 	} | ||||
| 	bool mute() const; | ||||
| 	bool changeMute(bool newMute); | ||||
| 	void addUnreadBar(); | ||||
| 	void destroyUnreadBar(); | ||||
|  | @ -212,46 +198,34 @@ public: | |||
| 	bool isReadyFor(MsgId msgId); // has messages for showing history at msgId
 | ||||
| 	void getReadyFor(MsgId msgId); | ||||
| 
 | ||||
| 	void setLastMessage(HistoryItem *msg); | ||||
| 	void fixLastMessage(bool wasAtBottom); | ||||
| 	HistoryItem *lastMessage() const; | ||||
| 	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 maxMsgId() const; | ||||
| 	MsgId msgIdForRead() const; | ||||
| 
 | ||||
| 	void resizeToWidth(int newWidth); | ||||
| 	int height() const; | ||||
| 
 | ||||
| 	void removeNotification(HistoryItem *item) { | ||||
| 		if (!notifies.isEmpty()) { | ||||
| 			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(); | ||||
| 	} | ||||
| 	void itemRemoved(not_null<HistoryItem*> item); | ||||
| 	void itemVanished(not_null<HistoryItem*> item); | ||||
| 
 | ||||
| 	bool hasPendingResizedItems() const { | ||||
| 		return _flags & Flag::f_has_pending_resized_items; | ||||
| 	} | ||||
| 	HistoryItem *currentNotification(); | ||||
| 	bool hasNotification() const; | ||||
| 	void skipNotification(); | ||||
| 	void popNotification(HistoryItem *item); | ||||
| 
 | ||||
| 	bool hasPendingResizedItems() const; | ||||
| 	void setHasPendingResizedItems(); | ||||
| 
 | ||||
| 	void paintDialog(Painter &p, int32 w, bool sel) const; | ||||
| 	bool mySendActionUpdated(SendAction::Type type, bool doing); | ||||
| 	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.
 | ||||
| 	std::deque<std::unique_ptr<HistoryBlock>> blocks; | ||||
| 
 | ||||
| 	int height() const; | ||||
| 	int32 msgCount = 0; | ||||
| 	MsgId inboxReadBefore = 1; | ||||
| 	MsgId outboxReadBefore = 1; | ||||
| 
 | ||||
| 	not_null<PeerData*> peer; | ||||
| 	bool oldLoaded = false; | ||||
| 	bool newLoaded = true; | ||||
| 	HistoryItem *lastMsg = nullptr; | ||||
| 	HistoryItem *lastSentMsg = nullptr; | ||||
| 
 | ||||
| 	typedef QList<HistoryItem*> NotifyQueue; | ||||
|  | @ -379,7 +347,17 @@ public: | |||
| 
 | ||||
| 	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
 | ||||
| 	// and scrollTopOffset remains the same
 | ||||
| 	// if we are at the bottom of the window scrollTopItem == nullptr and
 | ||||
|  | @ -389,7 +367,6 @@ protected: | |||
| 	// helper method for countScrollState(int top)
 | ||||
| 	void countScrollTopItem(int top); | ||||
| 
 | ||||
| 	void clearOnDestroy(); | ||||
| 	HistoryItem *addNewToLastBlock(const MTPMessage &msg, NewMessageType type); | ||||
| 
 | ||||
| 	// this method just removes a block from the blocks list
 | ||||
|  | @ -429,25 +406,18 @@ protected: | |||
| 		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( | ||||
| 		not_null<HistoryBlock*> block, | ||||
| 		not_null<Element*> view); | ||||
| 	void removeNotification(not_null<HistoryItem*> item); | ||||
| 
 | ||||
| 	QDateTime adjustChatListDate() const override; | ||||
| 	void changedInChatListHook(Dialogs::Mode list, bool added) override; | ||||
| 	void changedChatListPinHook() override; | ||||
| 
 | ||||
| 	void setInboxReadTill(MsgId upTo); | ||||
| 	void setOutboxReadTill(MsgId upTo); | ||||
| 
 | ||||
| 	void applyMessageChanges( | ||||
| 		not_null<HistoryItem*> item, | ||||
| 		const MTPMessage &original); | ||||
|  | @ -455,14 +425,13 @@ private: | |||
| 		not_null<HistoryItem*> item, | ||||
| 		const MTPDmessageService &data); | ||||
| 
 | ||||
| 	// After adding a new history slice check the lastMsg and newLoaded.
 | ||||
| 	void checkLastMsg(); | ||||
| 	// After adding a new history slice check the lastMessage and newLoaded.
 | ||||
| 	void checkLastMessage(); | ||||
| 	void setLastMessage(HistoryItem *item); | ||||
| 
 | ||||
| 	// Add all items to the unread mentions if we were not loaded at bottom and now are.
 | ||||
| 	void checkAddAllToUnreadMentions(); | ||||
| 
 | ||||
| 	template <int kSharedMediaTypeCount> | ||||
| 	void addToSharedMedia(std::vector<MsgId> (&medias)[kSharedMediaTypeCount], bool force); | ||||
| 	void addToSharedMedia(const std::vector<not_null<HistoryItem*>> &items); | ||||
| 	void addEdgesToSharedMedia(); | ||||
| 
 | ||||
|  | @ -480,14 +449,18 @@ private: | |||
| 
 | ||||
| 	Flags _flags = 0; | ||||
| 	bool _mute = false; | ||||
| 	int _unreadCount = 0; | ||||
| 	int _width = 0; | ||||
| 	int _height = 0; | ||||
| 	Element *_unreadBarView = nullptr; | ||||
| 	Element *_firstUnreadView = nullptr; | ||||
| 	HistoryService *_joinedMessage = nullptr; | ||||
| 
 | ||||
| 	base::optional<MsgId> _inboxReadBefore; | ||||
| 	base::optional<MsgId> _outboxReadBefore; | ||||
| 	base::optional<int> _unreadCount; | ||||
| 	base::optional<int> _unreadMentionsCount; | ||||
| 	base::flat_set<MsgId> _unreadMentions; | ||||
| 	base::optional<HistoryItem*> _lastMessage; | ||||
| 
 | ||||
| 	// A pointer to the block that is currently being built.
 | ||||
| 	// 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 { | ||||
| public: | ||||
| 	using Element = HistoryView::Element; | ||||
|  | @ -559,7 +501,6 @@ public: | |||
| 
 | ||||
| 	std::vector<std::unique_ptr<Element>> messages; | ||||
| 
 | ||||
| 	void clear(bool leaveItems = false); | ||||
| 	void remove(not_null<Element*> view); | ||||
| 	void refreshView(not_null<Element*> view); | ||||
| 
 | ||||
|  |  | |||
|  | @ -178,11 +178,15 @@ HistoryInner::HistoryInner( | |||
| 	}, lifetime()); | ||||
| } | ||||
| 
 | ||||
| void HistoryInner::messagesReceived(PeerData *peer, const QVector<MTPMessage> &messages) { | ||||
| void HistoryInner::messagesReceived( | ||||
| 		PeerData *peer, | ||||
| 		const QVector<MTPMessage> &messages) { | ||||
| 	if (_history && _history->peer == peer) { | ||||
| 		_history->addOlderSlice(messages); | ||||
| 	} else if (_migrated && _migrated->peer == peer) { | ||||
| 		bool newLoaded = (_migrated && _migrated->isEmpty() && !_history->isEmpty()); | ||||
| 		const auto newLoaded = _migrated | ||||
| 			&& _migrated->isEmpty() | ||||
| 			&& !_history->isEmpty(); | ||||
| 		_migrated->addOlderSlice(messages); | ||||
| 		if (newLoaded) { | ||||
| 			_migrated->addNewerSlice(QVector<MTPMessage>()); | ||||
|  |  | |||
|  | @ -148,19 +148,7 @@ void HistoryItem::invalidateChatsListEntry() { | |||
| 
 | ||||
| void HistoryItem::finishEditionToEmpty() { | ||||
| 	finishEdition(-1); | ||||
| 
 | ||||
| 	_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); | ||||
| 	} | ||||
| 	_history->itemVanished(this); | ||||
| } | ||||
| 
 | ||||
| bool HistoryItem::isMediaUnread() const { | ||||
|  | @ -244,7 +232,7 @@ void HistoryItem::destroy() { | |||
| 	if (isLogEntry()) { | ||||
| 		Assert(!mainView()); | ||||
| 	} 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(); | ||||
| 		if (IsServerMsgId(id)) { | ||||
| 			if (const auto types = sharedMediaTypes()) { | ||||
|  | @ -256,32 +244,7 @@ void HistoryItem::destroy() { | |||
| 		} else { | ||||
| 			Auth().api().cancelLocalItem(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
 | ||||
| 			} | ||||
| 		} | ||||
| 		_history->itemRemoved(this); | ||||
| 	} | ||||
| 	delete this; | ||||
| } | ||||
|  | @ -295,9 +258,6 @@ void HistoryItem::refreshMainView() { | |||
| 
 | ||||
| void HistoryItem::removeMainView() { | ||||
| 	if (const auto view = mainView()) { | ||||
| 		if (const auto channelHistory = _history->asChannelHistory()) { | ||||
| 			channelHistory->messageDetached(this); | ||||
| 		} | ||||
| 		Auth().data().notifyHistoryChangeDelayed(_history); | ||||
| 		view->removeFromBlock(); | ||||
| 	} | ||||
|  | @ -551,11 +511,11 @@ bool HistoryItem::unread() const { | |||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		if (id > 0) { | ||||
| 			if (id < history()->outboxReadBefore) { | ||||
| 		if (IsServerMsgId(id)) { | ||||
| 			if (!history()->isServerSideUnread(this)) { | ||||
| 				return false; | ||||
| 			} | ||||
| 			if (auto user = history()->peer->asUser()) { | ||||
| 			if (const auto user = history()->peer->asUser()) { | ||||
| 				if (user->botInfo) { | ||||
| 					return false; | ||||
| 				} | ||||
|  | @ -568,8 +528,8 @@ bool HistoryItem::unread() const { | |||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	if (id > 0) { | ||||
| 		if (id < history()->inboxReadBefore) { | ||||
| 	if (IsServerMsgId(id)) { | ||||
| 		if (!history()->isServerSideUnread(this)) { | ||||
| 			return false; | ||||
| 		} | ||||
| 		return true; | ||||
|  | @ -587,24 +547,6 @@ bool HistoryItem::isEmpty() const { | |||
| 		&& !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 { | ||||
| 	auto getText = [this]() { | ||||
| 		if (_media) { | ||||
|  |  | |||
|  | @ -254,9 +254,6 @@ public: | |||
| 
 | ||||
| 	MessageGroupId groupId() const; | ||||
| 
 | ||||
| 	HistoryItem *previousItem() const; | ||||
| 	HistoryItem *nextItem() const; | ||||
| 
 | ||||
| 	virtual std::unique_ptr<HistoryView::Element> createView( | ||||
| 		not_null<HistoryView::ElementDelegate*> delegate) = 0; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2256,9 +2256,7 @@ void HistoryWidget::historyToDown(History *history) { | |||
| void HistoryWidget::unreadCountChanged(not_null<History*> history) { | ||||
| 	if (history == _history || history == _migrated) { | ||||
| 		updateHistoryDownVisibility(); | ||||
| 		_historyDown->setUnreadCount( | ||||
| 			_history->unreadCount() | ||||
| 			+ (_migrated ? _migrated->unreadCount() : 0)); | ||||
| 		_historyDown->setUnreadCount(_history->chatListUnreadCount()); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -2402,9 +2400,9 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages | |||
| 		if (_history->loadedAtBottom() && App::wnd()) App::wnd()->checkHistoryActivation(); | ||||
| 	} else if (_firstLoadRequest == requestId) { | ||||
| 		if (toMigrated) { | ||||
| 			_history->clear(true); | ||||
| 			_history->unloadBlocks(); | ||||
| 		} else if (_migrated) { | ||||
| 			_migrated->clear(true); | ||||
| 			_migrated->unloadBlocks(); | ||||
| 		} | ||||
| 		addMessagesToFront(peer, *histList); | ||||
| 		_firstLoadRequest = 0; | ||||
|  | @ -2421,9 +2419,9 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages | |||
| 		historyLoaded(); | ||||
| 	} else if (_delayedShowAtRequest == requestId) { | ||||
| 		if (toMigrated) { | ||||
| 			_history->clear(true); | ||||
| 			_history->unloadBlocks(); | ||||
| 		} else if (_migrated) { | ||||
| 			_migrated->clear(true); | ||||
| 			_migrated->unloadBlocks(); | ||||
| 		} | ||||
| 
 | ||||
| 		_delayedShowAtRequest = 0; | ||||
|  | @ -2506,15 +2504,15 @@ void HistoryWidget::firstLoadMessages() { | |||
| 	auto offset = 0; | ||||
| 	auto loadCount = kMessagesPerPage; | ||||
| 	if (_showAtMsgId == ShowAtUnreadMsgId) { | ||||
| 		if (_migrated && _migrated->unreadCount()) { | ||||
| 		if (const auto around = _migrated ? _migrated->loadAroundId() : 0) { | ||||
| 			_history->getReadyFor(_showAtMsgId); | ||||
| 			from = _migrated->peer; | ||||
| 			offset = -loadCount / 2; | ||||
| 			offsetId = _migrated->inboxReadBefore; | ||||
| 		} else if (_history->unreadCount()) { | ||||
| 			offsetId = around; | ||||
| 		} else if (const auto around = _history->loadAroundId()) { | ||||
| 			_history->getReadyFor(_showAtMsgId); | ||||
| 			offset = -loadCount / 2; | ||||
| 			offsetId = _history->inboxReadBefore; | ||||
| 			offsetId = around; | ||||
| 		} else { | ||||
| 			_history->getReadyFor(ShowAtTheEndMsgId); | ||||
| 		} | ||||
|  | @ -2651,13 +2649,13 @@ void HistoryWidget::delayedShowAt(MsgId showAtMsgId) { | |||
| 	auto offset = 0; | ||||
| 	auto loadCount = kMessagesPerPage; | ||||
| 	if (_delayedShowAtMsgId == ShowAtUnreadMsgId) { | ||||
| 		if (_migrated && _migrated->unreadCount()) { | ||||
| 		if (const auto around = _migrated ? _migrated->loadAroundId() : 0) { | ||||
| 			from = _migrated->peer; | ||||
| 			offset = -loadCount / 2; | ||||
| 			offsetId = _migrated->inboxReadBefore; | ||||
| 		} else if (_history->unreadCount()) { | ||||
| 			offsetId = around; | ||||
| 		} else if (const auto around = _history->loadAroundId()) { | ||||
| 			offset = -loadCount / 2; | ||||
| 			offsetId = _history->inboxReadBefore; | ||||
| 			offsetId = around; | ||||
| 		} else { | ||||
| 			loadCount = kMessagesPerPageFirst; | ||||
| 		} | ||||
|  | @ -3630,8 +3628,17 @@ bool HistoryWidget::inlineBotResolveFail(QString name, const RPCError &error) { | |||
| } | ||||
| 
 | ||||
| bool HistoryWidget::isBotStart() const { | ||||
| 	if (!_peer || !_peer->isUser() || !_peer->asUser()->botInfo || !_canSendMessages) return false; | ||||
| 	return !_peer->asUser()->botInfo->startToken.isEmpty() || (_history->isEmpty() && !_history->lastMsg); | ||||
| 	const auto user = _peer ? _peer->asUser() : nullptr; | ||||
| 	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 { | ||||
|  | @ -5126,14 +5133,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { | |||
| 		if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { | ||||
| 			_scroll->keyPressEvent(e); | ||||
| 		} else if ((e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier)) == Qt::ControlModifier) { | ||||
| 			if (_history && _history->lastMsg && !_editMsgId) { | ||||
| 				if (const auto item = App::histItemById(_history->channelId(), _replyToId)) { | ||||
| 					if (const auto next = item->nextItem()) { | ||||
| 						Ui::showPeerHistory(_peer, next->id); | ||||
| 						replyToMessage(next); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			replyToNextMessage(); | ||||
| 		} | ||||
| 	} else if (e->key() == Qt::Key_Up) { | ||||
| 		if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { | ||||
|  | @ -5145,17 +5145,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { | |||
| 			} | ||||
| 			_scroll->keyPressEvent(e); | ||||
| 		} else if ((e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier)) == Qt::ControlModifier) { | ||||
| 			if (_history && _history->lastMsg && !_editMsgId) { | ||||
| 				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); | ||||
| 				} | ||||
| 			} | ||||
| 			replyToPreviousMessage(); | ||||
| 		} | ||||
| 	} else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { | ||||
| 		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() { | ||||
| 	if (!_fieldAutocomplete->isHidden()) { | ||||
| 		_fieldAutocomplete->chooseSelected(FieldAutocomplete::ChooseMethod::ByTab); | ||||
|  |  | |||
|  | @ -538,6 +538,8 @@ private: | |||
| 	void applyInlineBotQuery(UserData *bot, const QString &query); | ||||
| 
 | ||||
| 	void cancelReplyAfterMediaSend(bool lastKeyboardUsed); | ||||
| 	void replyToPreviousMessage(); | ||||
| 	void replyToNextMessage(); | ||||
| 
 | ||||
| 	void hideSelectorControlsAnimated(); | ||||
| 	int countMembersDropdownHeightMax() const; | ||||
|  |  | |||
|  | @ -611,6 +611,9 @@ void Element::clickHandlerPressedChanged( | |||
| } | ||||
| 
 | ||||
| Element::~Element() { | ||||
| 	if (_data->mainView() == this) { | ||||
| 		_data->clearMainView(); | ||||
| 	} | ||||
| 	Auth().data().unregisterItemView(this); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -887,21 +887,27 @@ void MainWidget::deleteLayer(FullMsgId itemId) { | |||
| void MainWidget::cancelUploadLayer(not_null<HistoryItem*> item) { | ||||
| 	const auto itemId = item->fullId(); | ||||
| 	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(); | ||||
| 		if (const auto item = App::histItemById(itemId)) { | ||||
| 			const auto history = item->history(); | ||||
| 			const auto wasLast = (history->lastMsg == item); | ||||
| 			item->destroy(); | ||||
| 			if (wasLast && !history->lastMsg) { | ||||
| 				checkPeerHistory(history->peer); | ||||
| 			if (!history->lastMessageKnown()) { | ||||
| 				Auth().api().requestDialogEntry(history); | ||||
| 			} | ||||
| 			Auth().data().sendHistoryChangeNotifications(); | ||||
| 		} | ||||
| 		Auth().uploader().unpause(); | ||||
| 	}), base::lambda_guarded(this, [] { | ||||
| 	}; | ||||
| 	const auto continueUpload = [=] { | ||||
| 		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) { | ||||
|  | @ -1041,12 +1047,11 @@ void MainWidget::deleteConversation( | |||
| 	if (const auto history = App::historyLoaded(peer->id)) { | ||||
| 		Auth().data().setPinnedDialog(history, false); | ||||
| 		removeDialog(history); | ||||
| 		if (peer->isMegagroup() && peer->asChannel()->mgInfo->migrateFromPtr) { | ||||
| 			if (auto migrated = App::historyLoaded(peer->asChannel()->mgInfo->migrateFromPtr->id)) { | ||||
| 				if (migrated->lastMsg) { // return initial dialog
 | ||||
| 					migrated->setLastMessage(migrated->lastMsg); | ||||
| 				} else { | ||||
| 					checkPeerHistory(migrated->peer); | ||||
| 		if (const auto channel = peer->asMegagroup()) { | ||||
| 			channel->addFlags(MTPDchannel::Flag::f_left); | ||||
| 			if (const auto from = channel->mgInfo->migrateFromPtr) { | ||||
| 				if (const auto migrated = App::historyLoaded(from)) { | ||||
| 					migrated->updateChatListExistence(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | @ -1059,7 +1064,12 @@ void MainWidget::deleteConversation( | |||
| 	} | ||||
| 	if (deleteHistory) { | ||||
| 		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)); | ||||
| } | ||||
| 
 | ||||
| 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( | ||||
| 		not_null<PeerData*> chatOrChannel, | ||||
| 		const std::vector<not_null<UserData*>> &users) { | ||||
|  | @ -1194,99 +1163,6 @@ bool MainWidget::addParticipantsFail( | |||
| 	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) { | ||||
| 	if (MTP::isDefaultHandledError(error)) return false; | ||||
| 
 | ||||
|  | @ -1562,9 +1438,9 @@ void MainWidget::messagesAffected( | |||
| 		ptsUpdateAndApply(data.vpts.v, data.vpts_count.v); | ||||
| 	} | ||||
| 
 | ||||
| 	if (auto h = App::historyLoaded(peer ? peer->id : 0)) { | ||||
| 		if (!h->lastMsg) { | ||||
| 			checkPeerHistory(peer); | ||||
| 	if (const auto history = App::historyLoaded(peer)) { | ||||
| 		if (!history->lastMessageKnown()) { | ||||
| 			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); | ||||
| 
 | ||||
| 	int32 timeout = 0; | ||||
|  | @ -3491,28 +3369,28 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha | |||
| 
 | ||||
| 		App::feedUsers(d.vusers); | ||||
| 		App::feedChats(d.vchats); | ||||
| 		auto h = App::historyLoaded(channel->id); | ||||
| 		if (h) { | ||||
| 			h->setNotLoadedAtBottom(); | ||||
| 		auto history = App::historyLoaded(channel->id); | ||||
| 		if (history) { | ||||
| 			history->setNotLoadedAtBottom(); | ||||
| 		} | ||||
| 		App::feedMsgs(d.vmessages, NewMessageLast); | ||||
| 		if (h) { | ||||
| 			if (auto item = App::histItemById(peerToChannel(channel->id), d.vtop_message.v)) { | ||||
| 				h->setLastMessage(item); | ||||
| 			} | ||||
| 			if (d.vunread_count.v >= h->unreadCount()) { | ||||
| 				h->setUnreadCount(d.vunread_count.v); | ||||
| 				h->inboxReadBefore = d.vread_inbox_max_id.v + 1; | ||||
| 			} | ||||
| 			h->setUnreadMentionsCount(d.vunread_mentions_count.v); | ||||
| 		if (history) { | ||||
| 			history->applyDialogFields( | ||||
| 				d.vunread_count.v, | ||||
| 				d.vread_inbox_max_id.v, | ||||
| 				d.vread_outbox_max_id.v); | ||||
| 			history->applyDialogTopMessage(d.vtop_message.v); | ||||
| 			history->setUnreadMentionsCount(d.vunread_mentions_count.v); | ||||
| 			if (_history->peer() == channel) { | ||||
| 				_history->updateHistoryDownVisibility(); | ||||
| 				_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(); | ||||
| 		channel->ptsInit(d.vpts.v); | ||||
| 	} break; | ||||
|  | @ -3520,39 +3398,7 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha | |||
| 	case mtpc_updates_channelDifference: { | ||||
| 		auto &d = diff.c_updates_channelDifference(); | ||||
| 
 | ||||
| 		App::feedUsers(d.vusers); | ||||
| 		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; | ||||
| 		feedChannelDifference(d); | ||||
| 
 | ||||
| 		if (d.has_timeout()) timeout = d.vtimeout.v; | ||||
| 		isFinal = d.is_final(); | ||||
|  | @ -3570,55 +3416,16 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| void MainWidget::gotRangeDifference( | ||||
| 		ChannelData *channel, | ||||
| 		const MTPupdates_ChannelDifference &diff) { | ||||
| 	int32 nextRequestPts = 0; | ||||
| 	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); | ||||
| void MainWidget::feedChannelDifference( | ||||
| 		const MTPDupdates_channelDifference &data) { | ||||
| 	App::feedUsers(data.vusers); | ||||
| 	App::feedChats(data.vchats); | ||||
| 
 | ||||
| 	_handlingChannelDifference = true; | ||||
| 		feedMessageIds(d.vother_updates); | ||||
| 		App::feedMsgs(d.vnew_messages, NewMessageUnread); | ||||
| 		feedUpdateVector(d.vother_updates, true); | ||||
| 	feedMessageIds(data.vother_updates); | ||||
| 	App::feedMsgs(data.vnew_messages, NewMessageUnread); | ||||
| 	feedUpdateVector(data.vother_updates, true); | ||||
| 	_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) { | ||||
|  | @ -3985,12 +3792,12 @@ void MainWidget::onSelfParticipantUpdated(ChannelData *channel) { | |||
| 			history = App::history(channel); | ||||
| 		} | ||||
| 		if (history->isEmpty()) { | ||||
| 			checkPeerHistory(channel); | ||||
| 			Auth().api().requestDialogEntry(history); | ||||
| 		} else { | ||||
| 			history->asChannelHistory()->checkJoinedMessage(true); | ||||
| 			history->checkJoinedMessage(true); | ||||
| 		} | ||||
| 	} else if (history) { | ||||
| 		history->asChannelHistory()->checkJoinedMessage(); | ||||
| 		history->checkJoinedMessage(); | ||||
| 	} | ||||
| 	Auth().data().sendHistoryChangeNotifications(); | ||||
| } | ||||
|  | @ -4768,10 +4575,9 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { | |||
| 				const auto existing = App::histItemById(channel, newId); | ||||
| 				if (existing && !local->mainView()) { | ||||
| 					const auto history = local->history(); | ||||
| 					const auto wasLast = (history->lastMsg == local); | ||||
| 					local->destroy(); | ||||
| 					if (wasLast && !history->lastMsg) { | ||||
| 						checkPeerHistory(history->peer); | ||||
| 					if (!history->lastMessageKnown()) { | ||||
| 						Auth().api().requestDialogEntry(history); | ||||
| 					} | ||||
| 				} else { | ||||
| 					if (existing) { | ||||
|  | @ -5211,7 +5017,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { | |||
| 
 | ||||
| 	case mtpc_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; | ||||
| 			if (!channel->amIn()) { | ||||
| 				if (const auto history = App::historyLoaded(channel->id)) { | ||||
|  |  | |||
|  | @ -215,9 +215,6 @@ public: | |||
| 		not_null<ChannelData*> channel, | ||||
| 		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); | ||||
| 
 | ||||
| 	Dialogs::IndexedList *contactsList(); | ||||
|  | @ -288,8 +285,8 @@ public: | |||
| 
 | ||||
| 	void scheduleViewIncrement(HistoryItem *item); | ||||
| 
 | ||||
| 	void gotRangeDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff); | ||||
| 	void onSelfParticipantUpdated(ChannelData *channel); | ||||
| 	void feedChannelDifference(const MTPDupdates_channelDifference &data); | ||||
| 
 | ||||
| 	// Mayde public for ApiWrap, while it is still here.
 | ||||
| 	// Better would be for this to be moved to ApiWrap.
 | ||||
|  | @ -478,7 +475,6 @@ private: | |||
| 	void feedUpdate(const MTPUpdate &update); | ||||
| 
 | ||||
| 	void deleteHistoryPart(DeleteHistoryRequest request, const MTPmessages_AffectedHistory &result); | ||||
| 	void deleteAllFromUserPart(DeleteAllFromUserParams params, const MTPmessages_AffectedHistory &result); | ||||
| 
 | ||||
| 	void updateReceived(const mtpPrime *from, const mtpPrime *end); | ||||
| 	bool updateFail(const RPCError &e); | ||||
|  |  | |||
|  | @ -593,7 +593,15 @@ void PeerMenuAddChannelMembers(not_null<ChannelData*> channel) { | |||
| } | ||||
| 
 | ||||
| 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) { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue