1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #pragma once
9 
10 #include "window/section_widget.h"
11 #include "window/section_memento.h"
12 #include "history/view/history_view_list_widget.h"
13 #include "data/data_messages.h"
14 #include "base/timer.h"
15 
16 class History;
17 enum class SendMediaType;
18 struct SendingAlbum;
19 
20 namespace SendMenu {
21 enum class Type;
22 } // namespace SendMenu
23 
24 namespace Api {
25 struct SendOptions;
26 } // namespace Api
27 
28 namespace Storage {
29 } // namespace Storage
30 
31 namespace Ui {
32 class ScrollArea;
33 class PlainShadow;
34 class FlatButton;
35 class HistoryDownButton;
36 class PinnedBar;
37 struct PreparedList;
38 class SendFilesWay;
39 } // namespace Ui
40 
41 namespace Profile {
42 class BackButton;
43 } // namespace Profile
44 
45 namespace InlineBots {
46 class Result;
47 } // namespace InlineBots
48 
49 namespace Data {
50 class RepliesList;
51 } // namespace Data
52 
53 namespace HistoryView {
54 
55 namespace Controls {
56 struct VoiceToSend;
57 } // namespace Controls
58 
59 class Element;
60 class TopBarWidget;
61 class RepliesMemento;
62 class ComposeControls;
63 class SendActionPainter;
64 
65 class RepliesWidget final
66 	: public Window::SectionWidget
67 	, private ListDelegate {
68 public:
69 	RepliesWidget(
70 		QWidget *parent,
71 		not_null<Window::SessionController*> controller,
72 		not_null<History*> history,
73 		MsgId rootId);
74 	~RepliesWidget();
75 
76 	[[nodiscard]] not_null<History*> history() const;
77 	Dialogs::RowDescriptor activeChat() const override;
78 	bool preventsClose(Fn<void()> &&continueCallback) const override;
79 
hasTopBarShadow()80 	bool hasTopBarShadow() const override {
81 		return true;
82 	}
83 
84 	QPixmap grabForShowAnimation(
85 		const Window::SectionSlideParams &params) override;
86 
87 	bool showInternal(
88 		not_null<Window::SectionMemento*> memento,
89 		const Window::SectionShow &params) override;
90 	std::shared_ptr<Window::SectionMemento> createMemento() override;
91 	bool showMessage(
92 		PeerId peerId,
93 		const Window::SectionShow &params,
94 		MsgId messageId) override;
95 
96 	Window::SectionActionResult sendBotCommand(
97 		Bot::SendCommandRequest request) override;
98 
99 	void setInternalState(
100 		const QRect &geometry,
101 		not_null<RepliesMemento*> memento);
102 
103 	// Tabbed selector management.
104 	bool pushTabbedSelectorToThirdSection(
105 		not_null<PeerData*> peer,
106 		const Window::SectionShow &params) override;
107 	bool returnTabbedSelector() override;
108 
109 	// Float player interface.
110 	bool floatPlayerHandleWheelEvent(QEvent *e) override;
111 	QRect floatPlayerAvailableRect() override;
112 
113 	// ListDelegate interface.
114 	Context listContext() override;
115 	void listScrollTo(int top) override;
116 	void listCancelRequest() override;
117 	void listDeleteRequest() override;
118 	rpl::producer<Data::MessagesSlice> listSource(
119 		Data::MessagePosition aroundId,
120 		int limitBefore,
121 		int limitAfter) override;
122 	bool listAllowsMultiSelect() override;
123 	bool listIsItemGoodForSelection(not_null<HistoryItem*> item) override;
124 	bool listIsLessInOrder(
125 		not_null<HistoryItem*> first,
126 		not_null<HistoryItem*> second) override;
127 	void listSelectionChanged(SelectedItems &&items) override;
128 	void listVisibleItemsChanged(HistoryItemsList &&items) override;
129 	MessagesBarData listMessagesBar(
130 		const std::vector<not_null<Element*>> &elements) override;
131 	void listContentRefreshed() override;
132 	ClickHandlerPtr listDateLink(not_null<Element*> view) override;
133 	bool listElementHideReply(not_null<const Element*> view) override;
134 	bool listElementShownUnread(not_null<const Element*> view) override;
135 	bool listIsGoodForAroundPosition(not_null<const Element*> view) override;
136 	void listSendBotCommand(
137 		const QString &command,
138 		const FullMsgId &context) override;
139 	void listHandleViaClick(not_null<UserData*> bot) override;
140 	not_null<Ui::ChatTheme*> listChatTheme() override;
141 
142 protected:
143 	void resizeEvent(QResizeEvent *e) override;
144 	void paintEvent(QPaintEvent *e) override;
145 
146 	void showAnimatedHook(
147 		const Window::SectionSlideParams &params) override;
148 	void showFinishedHook() override;
149 	void doSetInnerFocus() override;
150 
151 private:
152 	void onScroll();
153 	void updateInnerVisibleArea();
154 	void updateControlsGeometry();
155 	void updateAdaptiveLayout();
156 	void saveState(not_null<RepliesMemento*> memento);
157 	void restoreState(not_null<RepliesMemento*> memento);
158 	void showAtStart();
159 	void showAtEnd();
160 	void showAtPosition(
161 		Data::MessagePosition position,
162 		HistoryItem *originItem = nullptr);
163 	bool showAtPositionNow(
164 		Data::MessagePosition position,
165 		HistoryItem *originItem,
166 		anim::type animated = anim::type::normal);
167 	void finishSending();
168 
169 	void setupComposeControls();
170 
171 	void setupRoot();
172 	void setupRootView();
173 	void setupDragArea();
174 	void sendReadTillRequest();
175 	void readTill(not_null<HistoryItem*> item);
176 	[[nodiscard]] std::optional<int> computeUnreadCountLocally(
177 		MsgId afterId) const;
178 
179 	void setupScrollDownButton();
180 	void scrollDownClicked();
181 	void scrollDownAnimationFinish();
182 	void updateScrollDownVisibility();
183 	void updateScrollDownPosition();
184 	void updatePinnedVisibility();
185 
186 	void confirmDeleteSelected();
187 	void confirmForwardSelected();
188 	void clearSelected();
189 	void setPinnedVisibility(bool shown);
190 
191 	void send();
192 	void send(Api::SendOptions options);
193 	void sendVoice(Controls::VoiceToSend &&data);
194 	void edit(
195 		not_null<HistoryItem*> item,
196 		Api::SendOptions options,
197 		mtpRequestId *const saveEditMsgRequestId);
198 	void chooseAttach();
199 	[[nodiscard]] SendMenu::Type sendMenuType() const;
200 	[[nodiscard]] MsgId replyToId() const;
201 	[[nodiscard]] HistoryItem *lookupRoot() const;
202 	[[nodiscard]] bool computeAreComments() const;
203 	[[nodiscard]] std::optional<int> computeUnreadCount() const;
204 	void orderWidgets();
205 
206 	void pushReplyReturn(not_null<HistoryItem*> item);
207 	void computeCurrentReplyReturn();
208 	void calculateNextReplyReturn();
209 	void restoreReplyReturns(const std::vector<MsgId> &list);
210 	void checkReplyReturns();
211 	void recountChatWidth();
212 	void replyToMessage(FullMsgId itemId);
213 	void refreshTopBarActiveChat();
214 	void refreshUnreadCountBadge();
215 	void reloadUnreadCountIfNeeded();
216 
217 	void uploadFile(const QByteArray &fileContent, SendMediaType type);
218 	bool confirmSendingFiles(
219 		QImage &&image,
220 		QByteArray &&content,
221 		std::optional<bool> overrideSendImagesAsPhotos = std::nullopt,
222 		const QString &insertTextOnCancel = QString());
223 	bool confirmSendingFiles(
224 		Ui::PreparedList &&list,
225 		const QString &insertTextOnCancel = QString());
226 	bool confirmSendingFiles(
227 		not_null<const QMimeData*> data,
228 		std::optional<bool> overrideSendImagesAsPhotos = std::nullopt,
229 		const QString &insertTextOnCancel = QString());
230 	bool showSendingFilesError(const Ui::PreparedList &list) const;
231 	void sendingFilesConfirmed(
232 		Ui::PreparedList &&list,
233 		Ui::SendFilesWay way,
234 		TextWithTags &&caption,
235 		Api::SendOptions options,
236 		bool ctrlShiftEnter);
237 
238 	void sendExistingDocument(not_null<DocumentData*> document);
239 	bool sendExistingDocument(
240 		not_null<DocumentData*> document,
241 		Api::SendOptions options);
242 	void sendExistingPhoto(not_null<PhotoData*> photo);
243 	bool sendExistingPhoto(
244 		not_null<PhotoData*> photo,
245 		Api::SendOptions options);
246 	void sendInlineResult(
247 		not_null<InlineBots::Result*> result,
248 		not_null<UserData*> bot);
249 	void sendInlineResult(
250 		not_null<InlineBots::Result*> result,
251 		not_null<UserData*> bot,
252 		Api::SendOptions options);
253 
254 	[[nodiscard]] bool showSlowmodeError();
255 	[[nodiscard]] std::optional<QString> writeRestriction() const;
256 
257 	const not_null<History*> _history;
258 	const MsgId _rootId = 0;
259 	std::shared_ptr<Ui::ChatTheme> _theme;
260 	HistoryItem *_root = nullptr;
261 	std::shared_ptr<Data::RepliesList> _replies;
262 	rpl::variable<bool> _areComments = false;
263 	std::shared_ptr<SendActionPainter> _sendAction;
264 	QPointer<ListWidget> _inner;
265 	object_ptr<TopBarWidget> _topBar;
266 	object_ptr<Ui::PlainShadow> _topBarShadow;
267 	std::unique_ptr<ComposeControls> _composeControls;
268 	bool _skipScrollEvent = false;
269 
270 	std::unique_ptr<Ui::PinnedBar> _rootView;
271 	int _rootViewHeight = 0;
272 	bool _rootViewInited = false;
273 	rpl::variable<bool> _rootVisible = false;
274 
275 	std::unique_ptr<Ui::ScrollArea> _scroll;
276 
277 	std::vector<MsgId> _replyReturns;
278 	HistoryItem *_replyReturn = nullptr;
279 
280 	Ui::Animations::Simple _scrollDownShown;
281 	bool _scrollDownIsShown = false;
282 	object_ptr<Ui::HistoryDownButton> _scrollDown;
283 
284 	bool _choosingAttach = false;
285 
286 	base::Timer _readRequestTimer;
287 	bool _readRequestPending = false;
288 	mtpRequestId _readRequestId = 0;
289 
290 	mtpRequestId _reloadUnreadCountRequestId = 0;
291 	bool _loaded = false;
292 
293 };
294 
295 
296 class RepliesMemento : public Window::SectionMemento {
297 public:
298 	RepliesMemento(
299 		not_null<History*> history,
300 		MsgId rootId,
301 		MsgId highlightId = 0)
_history(history)302 	: _history(history)
303 	, _rootId(rootId)
304 	, _highlightId(highlightId) {
305 	}
306 	explicit RepliesMemento(
307 		not_null<HistoryItem*> commentsItem,
308 		MsgId commentId = 0);
309 
310 	object_ptr<Window::SectionWidget> createWidget(
311 		QWidget *parent,
312 		not_null<Window::SessionController*> controller,
313 		Window::Column column,
314 		const QRect &geometry) override;
315 
getHistory()316 	[[nodiscard]] not_null<History*> getHistory() const {
317 		return _history;
318 	}
getRootId()319 	[[nodiscard]] MsgId getRootId() const {
320 		return _rootId;
321 	}
322 
setReplies(std::shared_ptr<Data::RepliesList> replies)323 	void setReplies(std::shared_ptr<Data::RepliesList> replies) {
324 		_replies = std::move(replies);
325 	}
getReplies()326 	[[nodiscard]] std::shared_ptr<Data::RepliesList> getReplies() const {
327 		return _replies;
328 	}
329 
setReplyReturns(const std::vector<MsgId> & list)330 	void setReplyReturns(const std::vector<MsgId> &list) {
331 		_replyReturns = list;
332 	}
replyReturns()333 	const std::vector<MsgId> &replyReturns() const {
334 		return _replyReturns;
335 	}
336 
list()337 	[[nodiscard]] not_null<ListMemento*> list() {
338 		return &_list;
339 	}
getHighlightId()340 	[[nodiscard]] MsgId getHighlightId() const {
341 		return _highlightId;
342 	}
343 
344 private:
345 	const not_null<History*> _history;
346 	const MsgId _rootId = 0;
347 	const MsgId _highlightId = 0;
348 	ListMemento _list;
349 	std::shared_ptr<Data::RepliesList> _replies;
350 	std::vector<MsgId> _replyReturns;
351 
352 };
353 
354 } // namespace HistoryView
355