Compare commits

...

130 Commits

Author SHA1 Message Date
John Preston
f98fdeab3f Alpha version 1.2.9.
- Quick Reply. Double click near a message for a quick reply.
- Search for Stickers. Click on the new search icon to access
your sticker sets or find new ones.
2018-03-08 01:45:46 +03:00
John Preston
90179188b9 Improve sticker by emoji ordering.
First display recent by send/install date, then trending, then other.
2018-03-08 00:26:35 +03:00
John Preston
ccef155f7a Receive and track recent sticker usage date. 2018-03-07 20:43:26 +03:00
John Preston
f0a95032a5 Show cloud stickers by emoji. 2018-03-07 16:53:12 +03:00
John Preston
c3ff5f2603 Limit amount of displayed recent stickers. 2018-03-07 15:04:05 +03:00
John Preston
ee182ea684 API scheme downgraded to layer 76. 2018-03-06 20:48:24 +03:00
John Preston
7f73cc3085 Fix file media with views counter layout. 2018-03-06 19:14:39 +03:00
John Preston
dcf70b2847 Add ".download" for .lnk and .scf file names.
This is a workaround for some windows shell vulnerabilities.

See http://www.defensecode.com/whitepapers/
Stealing-Windows-Credentials-Using-Google-Chrome.pdf
2018-03-06 19:04:47 +03:00
John Preston
cb5ba7edda Use a separate lang key for sticker pack remove. 2018-03-06 18:41:52 +03:00
John Preston
7940ef24ab Disallow hashtags of digits only. 2018-03-06 18:29:45 +03:00
John Preston
0f901b3728 Update API and use WebDocument for inline bots. 2018-03-06 16:49:44 +03:00
John Preston
09aba596ac Open t.me/iv links in browser. 2018-03-04 15:52:00 +03:00
John Preston
b930ac7bf9 Don't add unread mentions in channels. 2018-03-03 16:09:31 +03:00
John Preston
7f1bc4635a Update libtgvoip. 2018-03-03 16:01:22 +03:00
John Preston
d4253d2025 Fix reply previews display. 2018-03-03 15:55:53 +03:00
John Preston
b007fcb537 Add sticker sets search. 2018-03-03 14:21:32 +03:00
John Preston
e6dd7d7684 Fix crash in item view refresh. 2018-03-01 20:17:39 +03:00
John Preston
128663d95b Reply by double clicking a message. 2018-02-28 15:07:15 +03:00
John Preston
ef8b6d1a3d Process currency amounts before display. 2018-02-28 15:06:57 +03:00
John Preston
b4baebc230 Fix mediaview download icon in night mode. 2018-02-24 16:38:51 +03:00
John Preston
b4581a7bbf Closed beta 1.2.8.11. 2018-02-24 02:47:33 +03:00
John Preston
a285dca39e Apply webpage media from sent message via bot. 2018-02-24 02:46:35 +03:00
John Preston
00aa6d5ac3 Allow monospace block to end on ellipsis. 2018-02-22 21:16:01 +03:00
John Preston
027db285bc Add Info::Channels section + feed channels search. 2018-02-22 20:38:00 +03:00
John Preston
c3c9ba7e51 Add feed icons. 2018-02-22 20:37:49 +03:00
John Preston
a1be63f890 Jump to date by date click in feed. 2018-02-22 15:35:46 +03:00
John Preston
f066f3f139 Enable jump to date in feed. 2018-02-22 00:17:36 +03:00
John Preston
e17dcbb8eb Closed beta 1.2.8.10: API updated to layer 77. 2018-02-21 16:56:43 +03:00
John Preston
1ae22c8606 Fix assertion violation when no feed. 2018-02-21 16:38:56 +03:00
John Preston
d5569487a4 Closed beta 1.2.8.9. 2018-02-20 20:55:20 +03:00
John Preston
336e691dbc Add unread counter from feed to common counter. 2018-02-20 20:53:55 +03:00
John Preston
17a4d19beb Add a create feed channels list box. 2018-02-20 19:56:41 +03:00
John Preston
74aa1ad71e Ungroup all feed channels from context menu. 2018-02-18 17:00:14 +03:00
John Preston
f8c2f339a0 Create changelogs after data in AuthSession. 2018-02-18 16:26:28 +03:00
John Preston
1dd66184a1 Fix assertion violation on hashtag click. 2018-02-18 16:23:30 +03:00
John Preston
ddab8c1473 Fix fast share button hover area. 2018-02-18 16:22:58 +03:00
John Preston
49d2c97ceb Closed beta 1.2.8.8. 2018-02-16 21:00:19 +03:00
John Preston
351a423337 Highlight found messages in feed. 2018-02-16 20:59:35 +03:00
John Preston
07528be1e6 Support search in feed + scroll to search result. 2018-02-16 19:45:58 +03:00
John Preston
bc171f625a Remove some more symbols from filenames. 2018-02-15 15:34:38 +03:00
John Preston
0f775e1e66 Support feeds search display in dialogs list. 2018-02-14 23:18:21 +03:00
John Preston
98fb874b29 Closed beta 1.2.8.7. 2018-02-13 19:11:32 +03:00
John Preston
cfd5c2a650 Add feed notifications edit box. 2018-02-13 19:11:00 +03:00
John Preston
22a5b7faf6 Fix GIF playback glitch after loading. 2018-02-13 12:48:42 +03:00
John Preston
fe262701c0 Add notification toggle in feed channels. 2018-02-12 18:52:55 +03:00
John Preston
e1f71d3919 Closed beta 1.2.8.6. 2018-02-10 01:15:16 +03:00
John Preston
906cb95e67 Channels list in feed info with leave channel. 2018-02-10 01:14:26 +03:00
John Preston
f23c23f696 Closed beta 1.2.8.5. 2018-02-08 12:20:55 +03:00
John Preston
99c686e3e1 Display feed channels list in feed info. 2018-02-08 12:20:55 +03:00
John Preston
a144e35f84 Add content to feed info cover widget. 2018-02-08 12:20:55 +03:00
John Preston
5a5c5782a9 Replace crl::on_main to InvokeQueued in some cases.
If the event loop is reentered from the call it is unsafe to crl::on_main.
For example NSOpenPanel lags terribly if it is shown from crl::on_main.
2018-02-08 12:20:55 +03:00
John Preston
a2a5c30e60 Closed beta 1.2.8.4. 2018-02-08 12:20:55 +03:00
John Preston
3c4c466f8e Add cashtags and bot allowed service messages. 2018-02-08 12:20:55 +03:00
John Preston
6726826c17 Display empty feed placeholder. 2018-02-08 12:20:55 +03:00
John Preston
e102cb1469 Handle channelDifferenceTooLong in media and feed. 2018-02-08 12:20:54 +03:00
John Preston
11671e85da Add scroll-to-down button to Feed. 2018-02-08 12:20:54 +03:00
John Preston
b8614c60f9 Closed beta 1.2.8.3. 2018-02-08 12:20:54 +03:00
John Preston
269defa82d Fix forwarded Saved Messages layout. 2018-02-08 12:20:54 +03:00
John Preston
8bacc74d8b Request dialog list entries when needed.
Also save the original server-side int32 date in HistoryItems.
2018-02-08 12:20:54 +03:00
John Preston
0c5efb935d Read feed while scrolling. 2018-02-08 12:20:54 +03:00
John Preston
a7f67c4bc9 Better chats list entries management.
Make unread counts and last message base::optional<>.
Remove ChannelHistory.
2018-02-08 12:20:54 +03:00
John Preston
edcaccba1f Limit media count in one HistoryGroupedMedia. 2018-02-08 12:20:54 +03:00
John Preston
5ebecb4de3 Display feed userpic in single column layout. 2018-02-08 12:20:53 +03:00
John Preston
9f3048c1dc Hide service messages from feed. 2018-02-08 12:20:53 +03:00
John Preston
2586268b81 Remove HistoryJoined, use plain HistoryService. 2018-02-08 12:20:53 +03:00
John Preston
280ddb4629 Request full feed channels list before messages. 2018-02-08 12:20:53 +03:00
John Preston
20889d7003 Mark history as having pending resized items. 2018-02-08 12:20:53 +03:00
John Preston
d4f4698c69 Closed beta 1.2.8.2. 2018-02-08 12:20:53 +03:00
John Preston
adcce61b52 Fix date/unread bar display in albums. 2018-02-08 12:20:53 +03:00
John Preston
17b913fb13 Fix feed messages loading both ways. 2018-02-08 12:20:53 +03:00
John Preston
366ea1edc3 Notify about feed channels list changes. 2018-02-08 12:20:53 +03:00
John Preston
3a5a002be2 Add crash debug information. 2018-02-08 12:20:53 +03:00
John Preston
533fba8c70 Improve message context menu in feed. 2018-02-08 12:20:53 +03:00
John Preston
7435bd7fb0 Implement drag-n-drop from HistoryView::ListWidget. 2018-02-08 12:20:53 +03:00
John Preston
681b9b5ba3 Improve text selection in bubbles. 2018-02-08 12:20:53 +03:00
John Preston
600737c44f Fix copy selected items text in old and new lists. 2018-02-08 12:20:53 +03:00
John Preston
e5f3bed801 Improve drag selection in HistoryView::ListWidget. 2018-02-08 12:20:52 +03:00
John Preston
2fdc3169ce Fix / improve support for album items selection. 2018-02-08 12:20:52 +03:00
John Preston
722264f634 Add /Qspectre Visual C++ compiler option. 2018-02-08 12:20:52 +03:00
John Preston
a858ab5d0b Fix crash in DocumentData destructor.
Keep AuthSession pointer in DocumentData for loader destruction.
2018-02-08 12:20:52 +03:00
John Preston
63c1212ef1 Allow multiple items selection in HistoryView. 2018-02-08 12:20:52 +03:00
John Preston
2aa477176c Fix build for Xcode / GCC. 2018-02-08 12:20:52 +03:00
John Preston
6bb39451ea Closed beta 1.2.8.1. 2018-02-08 12:20:52 +03:00
John Preston
099a3c4642 Fix paste of image from Firefox.
It sometimes adds a strange path to empty temp file to mime data.
2018-02-08 12:20:52 +03:00
John Preston
9515520088 Update API scheme. 2018-02-08 12:20:52 +03:00
John Preston
fe1a90bd39 Move message context menu to a separate module. 2018-02-08 12:20:52 +03:00
John Preston
65df137610 Add group/ungroup action in channel peer menu. 2018-02-08 12:20:51 +03:00
John Preston
ced0c4d8f0 Move HistoryMessageDate to view elements. 2018-02-08 12:20:51 +03:00
John Preston
f3804429e4 Update GSL submodule. 2018-02-08 12:20:51 +03:00
John Preston
a47981054f Feed info profile placeholder. 2018-02-08 12:20:51 +03:00
John Preston
b9ad8bb700 Feed top bar placeholder. 2018-02-08 12:20:51 +03:00
John Preston
47ad5ea98a Display active feed state in dialogs list. 2018-02-08 12:20:51 +03:00
John Preston
840b42934b Use server-side my_results in contacts.search. 2018-02-08 12:20:51 +03:00
John Preston
4527c03c0d Use "Feed" name for chats list index and search. 2018-02-08 12:20:51 +03:00
John Preston
89941a8e83 Fix layout update notifications in Info::Media. 2018-02-08 12:20:50 +03:00
John Preston
ebd4651ac2 Manage unread bar using HistoryView::Element-s. 2018-02-08 12:20:50 +03:00
John Preston
861ab85ca1 Fix voice/video messages in chats/feed. 2018-02-08 12:20:50 +03:00
John Preston
f9154c4ed0 Fix albums layout and editing in feed. 2018-02-08 12:20:50 +03:00
John Preston
b91ebad8be Improve items resize in history and feed. 2018-02-08 12:20:50 +03:00
John Preston
e6baf8ef5b Fix layout of some media, enable GIF autoplay. 2018-02-08 12:20:50 +03:00
John Preston
d326c7e3fa Remove HistoryItemInstantiated. 2018-02-08 12:20:49 +03:00
John Preston
950126865e Handle item view refresh, fix groups. 2018-02-08 12:20:49 +03:00
John Preston
91f369a0b3 Handle view resize/repaint requests for mainView. 2018-02-08 12:20:49 +03:00
John Preston
d1a9d3992b API scheme updated to layer 76. 2018-02-08 12:20:49 +03:00
John Preston
2dd2ad5cdb Replace peerMessagesUpdated with notifications. 2018-02-08 12:20:49 +03:00
John Preston
04c8c95634 Use notify* instead of mark* in Data::Session. 2018-02-08 12:20:49 +03:00
John Preston
8a56ede187 Move all (item/view/media) maps to Data::Session. 2018-02-08 12:20:49 +03:00
John Preston
7425e80f05 Use HistoryMedia as view, add Data::Media. 2018-02-08 12:20:48 +03:00
John Preston
97a9089ebf Move draw / getState code to HistoryView::Message.
Item dimensions broken for now.
Also remove history.h from pch.
2018-02-08 12:20:48 +03:00
John Preston
bee474f6e9 Remove history_item and layout from pch.
Also move some code to separate modules.
Also create history item views by Window::Controller.
2018-02-08 12:20:47 +03:00
John Preston
4740d44159 Make HistoryView::Message a ClickHandlerHost. 2018-02-08 12:20:47 +03:00
John Preston
062b0b2165 Save item views in App::*Item() variables.
Also remove App::contextItem.
Also use owning pointers for history context menus.
2018-02-08 12:20:47 +03:00
John Preston
8060cb7426 Start HistoryView::Message class for item view. 2018-02-08 12:20:47 +03:00
John Preston
794e31505b First version of feed section view. 2018-02-08 12:20:46 +03:00
John Preston
50b120bc22 Fix glitch in single column back button click. 2018-02-08 12:20:46 +03:00
John Preston
f0b2e445f6 Prepare dialogs to open feeds. 2018-02-08 12:20:46 +03:00
John Preston
782e70b171 Support basic feed display in chats list. 2018-02-08 12:20:46 +03:00
John Preston
9d2239291d Add support for pinned feeds management. 2018-02-08 12:20:46 +03:00
John Preston
a2891807f8 Prepare dialogs to hold a history or a feed. 2018-02-08 12:20:46 +03:00
John Preston
6a9556d42c Move non-settings session data to Data::Session.
Rename AuthSessionData to AuthSessionSettings, move data away.
2018-02-08 12:20:46 +03:00
John Preston
724fe65d72 Start feeds support. 2018-02-08 12:20:45 +03:00
John Preston
46612ef128 Remove Notify::userIsContactChanged().
Replace with Notify::peerUpdatedDelayed().
2018-02-08 12:20:45 +03:00
John Preston
139ef5411a Prepare code for dialogFeed handling. 2018-02-08 12:20:45 +03:00
John Preston
ac57000437 Move contacts list loading to ApiWrap. 2018-02-08 12:20:45 +03:00
John Preston
31234cb487 API scheme updated to layer 75. 2018-02-08 12:20:45 +03:00
John Preston
05e36a064f API scheme updated to layer 74. 2018-02-08 12:20:45 +03:00
John Preston
f88cbf3d4b Fix crash in case of incorrect Text entities. 2018-02-08 12:20:45 +03:00
John Preston
7814ee0f7a Fix building crashpad for macOS. Update instructions.
Some tests were disabled by a crashpad patch because the changes to make
them work with new SDK are relatively big and no need to backport them.

Fixes #4353.
2018-02-08 11:27:50 +03:00
Alexander Nestorov
9f4e5e4603 Fix xcode build instructions (#4345) 2018-01-26 03:55:31 +03:00
Vitaliy Rudnyh
810fb45750 building-xcode.md: Add depot_tools to $PATH (#4331)
Otherwise "crashpad" would not compile.
2018-01-23 04:08:33 +03:00
321 changed files with 33227 additions and 16032 deletions

View File

@@ -0,0 +1,107 @@
diff --git a/client/capture_context_mac_test.cc b/client/capture_context_mac_test.cc
index 436ac5ad..8e14fb9c 100644
--- a/client/capture_context_mac_test.cc
+++ b/client/capture_context_mac_test.cc
@@ -34,11 +34,11 @@ namespace {
// gtest assertions.
void SanityCheckContext(const NativeCPUContext& context) {
#if defined(ARCH_CPU_X86)
- ASSERT_EQ(x86_THREAD_STATE32, context.tsh.flavor);
- ASSERT_EQ(implicit_cast<int>(x86_THREAD_STATE32_COUNT), context.tsh.count);
+ ASSERT_EQ(implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE32), implicit_cast<thread_state_flavor_t>(context.tsh.flavor));
+ ASSERT_EQ(implicit_cast<uint32_t>(x86_THREAD_STATE32_COUNT), implicit_cast<uint32_t>(context.tsh.count));
#elif defined(ARCH_CPU_X86_64)
- ASSERT_EQ(x86_THREAD_STATE64, context.tsh.flavor);
- ASSERT_EQ(implicit_cast<int>(x86_THREAD_STATE64_COUNT), context.tsh.count);
+ ASSERT_EQ(implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64), implicit_cast<thread_state_flavor_t>(context.tsh.flavor));
+ ASSERT_EQ(implicit_cast<uint32_t>(x86_THREAD_STATE64_COUNT), implicit_cast<uint32_t>(context.tsh.count));
#endif
#if defined(ARCH_CPU_X86_FAMILY)
diff --git a/client/simulate_crash_mac.cc b/client/simulate_crash_mac.cc
index 7e279015..27864388 100644
--- a/client/simulate_crash_mac.cc
+++ b/client/simulate_crash_mac.cc
@@ -177,12 +177,12 @@ bool DeliverException(thread_t thread,
void SimulateCrash(const NativeCPUContext& cpu_context) {
#if defined(ARCH_CPU_X86)
- DCHECK_EQ(cpu_context.tsh.flavor,
+ DCHECK_EQ(implicit_cast<thread_state_flavor_t>(cpu_context.tsh.flavor),
implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE32));
DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count),
x86_THREAD_STATE32_COUNT);
#elif defined(ARCH_CPU_X86_64)
- DCHECK_EQ(cpu_context.tsh.flavor,
+ DCHECK_EQ(implicit_cast<thread_state_flavor_t>(cpu_context.tsh.flavor),
implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64));
DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count),
x86_THREAD_STATE64_COUNT);
diff --git a/client/simulate_crash_mac_test.cc b/client/simulate_crash_mac_test.cc
index 87c5f845..ca813e4c 100644
--- a/client/simulate_crash_mac_test.cc
+++ b/client/simulate_crash_mac_test.cc
@@ -130,12 +130,12 @@ class TestSimulateCrashMac final : public MachMultiprocess,
reinterpret_cast<const x86_thread_state*>(old_state);
switch (state->tsh.flavor) {
case x86_THREAD_STATE32:
- EXPECT_EQ(implicit_cast<int>(x86_THREAD_STATE32_COUNT),
- state->tsh.count);
+ EXPECT_EQ(implicit_cast<uint32_t>(x86_THREAD_STATE32_COUNT),
+ implicit_cast<uint32_t>(state->tsh.count));
break;
case x86_THREAD_STATE64:
- EXPECT_EQ(implicit_cast<int>(x86_THREAD_STATE64_COUNT),
- state->tsh.count);
+ EXPECT_EQ(implicit_cast<uint32_t>(x86_THREAD_STATE64_COUNT),
+ implicit_cast<uint32_t>(state->tsh.count));
break;
default:
ADD_FAILURE() << "unexpected tsh.flavor " << state->tsh.flavor;
@@ -149,12 +149,12 @@ class TestSimulateCrashMac final : public MachMultiprocess,
reinterpret_cast<const x86_float_state*>(old_state);
switch (state->fsh.flavor) {
case x86_FLOAT_STATE32:
- EXPECT_EQ(implicit_cast<int>(x86_FLOAT_STATE32_COUNT),
- state->fsh.count);
+ EXPECT_EQ(implicit_cast<uint32_t>(x86_FLOAT_STATE32_COUNT),
+ implicit_cast<uint32_t>(state->fsh.count));
break;
case x86_FLOAT_STATE64:
- EXPECT_EQ(implicit_cast<int>(x86_FLOAT_STATE64_COUNT),
- state->fsh.count);
+ EXPECT_EQ(implicit_cast<uint32_t>(x86_FLOAT_STATE64_COUNT),
+ implicit_cast<uint32_t>(state->fsh.count));
break;
default:
ADD_FAILURE() << "unexpected fsh.flavor " << state->fsh.flavor;
@@ -168,12 +168,12 @@ class TestSimulateCrashMac final : public MachMultiprocess,
reinterpret_cast<const x86_debug_state*>(old_state);
switch (state->dsh.flavor) {
case x86_DEBUG_STATE32:
- EXPECT_EQ(implicit_cast<int>(x86_DEBUG_STATE32_COUNT),
- state->dsh.count);
+ EXPECT_EQ(implicit_cast<uint32_t>(x86_DEBUG_STATE32_COUNT),
+ implicit_cast<uint32_t>(state->dsh.count));
break;
case x86_DEBUG_STATE64:
- EXPECT_EQ(implicit_cast<int>(x86_DEBUG_STATE64_COUNT),
- state->dsh.count);
+ EXPECT_EQ(implicit_cast<uint32_t>(x86_DEBUG_STATE64_COUNT),
+ implicit_cast<uint32_t>(state->dsh.count));
break;
default:
ADD_FAILURE() << "unexpected dsh.flavor " << state->dsh.flavor;
diff --git a/crashpad.gyp b/crashpad.gyp
index 42fe0a26..d8af1bf1 100644
--- a/crashpad.gyp
+++ b/crashpad.gyp
@@ -25,7 +25,7 @@
'minidump/minidump.gyp:*',
'minidump/minidump_test.gyp:*',
'snapshot/snapshot.gyp:*',
- 'snapshot/snapshot_test.gyp:*',
+# 'snapshot/snapshot_test.gyp:*',
'test/test.gyp:*',
'test/test_test.gyp:*',
'tools/tools.gyp:*',

View File

@@ -1,5 +1,5 @@
diff --git a/build/crashpad.gypi b/build/crashpad.gypi
index 027c7b6..4bfdfb5 100644
index 027c7b68..4bfdfb5a 100644
--- a/build/crashpad.gypi
+++ b/build/crashpad.gypi
@@ -25,5 +25,15 @@
@@ -18,3 +18,110 @@ index 027c7b6..4bfdfb5 100644
+ ],
},
}
diff --git a/client/capture_context_mac_test.cc b/client/capture_context_mac_test.cc
index 436ac5ad..8e14fb9c 100644
--- a/client/capture_context_mac_test.cc
+++ b/client/capture_context_mac_test.cc
@@ -34,11 +34,11 @@ namespace {
// gtest assertions.
void SanityCheckContext(const NativeCPUContext& context) {
#if defined(ARCH_CPU_X86)
- ASSERT_EQ(x86_THREAD_STATE32, context.tsh.flavor);
- ASSERT_EQ(implicit_cast<int>(x86_THREAD_STATE32_COUNT), context.tsh.count);
+ ASSERT_EQ(implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE32), implicit_cast<thread_state_flavor_t>(context.tsh.flavor));
+ ASSERT_EQ(implicit_cast<uint32_t>(x86_THREAD_STATE32_COUNT), implicit_cast<uint32_t>(context.tsh.count));
#elif defined(ARCH_CPU_X86_64)
- ASSERT_EQ(x86_THREAD_STATE64, context.tsh.flavor);
- ASSERT_EQ(implicit_cast<int>(x86_THREAD_STATE64_COUNT), context.tsh.count);
+ ASSERT_EQ(implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64), implicit_cast<thread_state_flavor_t>(context.tsh.flavor));
+ ASSERT_EQ(implicit_cast<uint32_t>(x86_THREAD_STATE64_COUNT), implicit_cast<uint32_t>(context.tsh.count));
#endif
#if defined(ARCH_CPU_X86_FAMILY)
diff --git a/client/simulate_crash_mac.cc b/client/simulate_crash_mac.cc
index 7e279015..27864388 100644
--- a/client/simulate_crash_mac.cc
+++ b/client/simulate_crash_mac.cc
@@ -177,12 +177,12 @@ bool DeliverException(thread_t thread,
void SimulateCrash(const NativeCPUContext& cpu_context) {
#if defined(ARCH_CPU_X86)
- DCHECK_EQ(cpu_context.tsh.flavor,
+ DCHECK_EQ(implicit_cast<thread_state_flavor_t>(cpu_context.tsh.flavor),
implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE32));
DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count),
x86_THREAD_STATE32_COUNT);
#elif defined(ARCH_CPU_X86_64)
- DCHECK_EQ(cpu_context.tsh.flavor,
+ DCHECK_EQ(implicit_cast<thread_state_flavor_t>(cpu_context.tsh.flavor),
implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64));
DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count),
x86_THREAD_STATE64_COUNT);
diff --git a/client/simulate_crash_mac_test.cc b/client/simulate_crash_mac_test.cc
index 87c5f845..ca813e4c 100644
--- a/client/simulate_crash_mac_test.cc
+++ b/client/simulate_crash_mac_test.cc
@@ -130,12 +130,12 @@ class TestSimulateCrashMac final : public MachMultiprocess,
reinterpret_cast<const x86_thread_state*>(old_state);
switch (state->tsh.flavor) {
case x86_THREAD_STATE32:
- EXPECT_EQ(implicit_cast<int>(x86_THREAD_STATE32_COUNT),
- state->tsh.count);
+ EXPECT_EQ(implicit_cast<uint32_t>(x86_THREAD_STATE32_COUNT),
+ implicit_cast<uint32_t>(state->tsh.count));
break;
case x86_THREAD_STATE64:
- EXPECT_EQ(implicit_cast<int>(x86_THREAD_STATE64_COUNT),
- state->tsh.count);
+ EXPECT_EQ(implicit_cast<uint32_t>(x86_THREAD_STATE64_COUNT),
+ implicit_cast<uint32_t>(state->tsh.count));
break;
default:
ADD_FAILURE() << "unexpected tsh.flavor " << state->tsh.flavor;
@@ -149,12 +149,12 @@ class TestSimulateCrashMac final : public MachMultiprocess,
reinterpret_cast<const x86_float_state*>(old_state);
switch (state->fsh.flavor) {
case x86_FLOAT_STATE32:
- EXPECT_EQ(implicit_cast<int>(x86_FLOAT_STATE32_COUNT),
- state->fsh.count);
+ EXPECT_EQ(implicit_cast<uint32_t>(x86_FLOAT_STATE32_COUNT),
+ implicit_cast<uint32_t>(state->fsh.count));
break;
case x86_FLOAT_STATE64:
- EXPECT_EQ(implicit_cast<int>(x86_FLOAT_STATE64_COUNT),
- state->fsh.count);
+ EXPECT_EQ(implicit_cast<uint32_t>(x86_FLOAT_STATE64_COUNT),
+ implicit_cast<uint32_t>(state->fsh.count));
break;
default:
ADD_FAILURE() << "unexpected fsh.flavor " << state->fsh.flavor;
@@ -168,12 +168,12 @@ class TestSimulateCrashMac final : public MachMultiprocess,
reinterpret_cast<const x86_debug_state*>(old_state);
switch (state->dsh.flavor) {
case x86_DEBUG_STATE32:
- EXPECT_EQ(implicit_cast<int>(x86_DEBUG_STATE32_COUNT),
- state->dsh.count);
+ EXPECT_EQ(implicit_cast<uint32_t>(x86_DEBUG_STATE32_COUNT),
+ implicit_cast<uint32_t>(state->dsh.count));
break;
case x86_DEBUG_STATE64:
- EXPECT_EQ(implicit_cast<int>(x86_DEBUG_STATE64_COUNT),
- state->dsh.count);
+ EXPECT_EQ(implicit_cast<uint32_t>(x86_DEBUG_STATE64_COUNT),
+ implicit_cast<uint32_t>(state->dsh.count));
break;
default:
ADD_FAILURE() << "unexpected dsh.flavor " << state->dsh.flavor;
diff --git a/crashpad.gyp b/crashpad.gyp
index 42fe0a26..d8af1bf1 100644
--- a/crashpad.gyp
+++ b/crashpad.gyp
@@ -25,7 +25,7 @@
'minidump/minidump.gyp:*',
'minidump/minidump_test.gyp:*',
'snapshot/snapshot.gyp:*',
- 'snapshot/snapshot_test.gyp:*',
+# 'snapshot/snapshot_test.gyp:*',
'test/test.gyp:*',
'test/test_test.gyp:*',
'tools/tools.gyp:*',

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -793,6 +793,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_payment_done_for" = "You have just successfully transferred {amount} to {user} for {invoice}";
"lng_action_took_screenshot" = "{from} took a screenshot!";
"lng_action_you_took_screenshot" = "You took a screenshot!";
"lng_action_bot_allowed_from_domain" = "You allowed this bot to message you when you logged in on {domain}.";
"lng_ttl_photo_received" = "{from} sent you a self-destructing photo. Please view it on your mobile.";
"lng_ttl_photo_sent" = "You sent a self-destructing photo.";
@@ -925,6 +926,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stickers_remove_group_set" = "Remove group sticker set?";
"lng_stickers_group_from_your" = "Choose from your stickers";
"lng_stickers_group_from_featured" = "Choose from trending stickers";
"lng_stickers_search_sets" = "Search sticker sets";
"lng_stickers_nothing_found" = "No stickers found";
"lng_stickers_remove_pack_confirm" = "Remove";
"lng_in_dlg_photo" = "Photo";
"lng_in_dlg_album" = "Album";
@@ -1024,6 +1028,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_user_action_upload_file" = "{user} is sending a file";
"lng_unread_bar#one" = "{count} unread message";
"lng_unread_bar#other" = "{count} unread messages";
"lng_unread_bar_some" = "Unread messages";
"lng_maps_point" = "Location";
"lng_save_photo" = "Save image";
@@ -1042,6 +1047,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_view_profile" = "View profile";
"lng_context_view_group" = "View group info";
"lng_context_view_channel" = "View channel info";
"lng_context_view_feed_info" = "View feed info";
"lng_context_pin_to_top" = "Pin to top";
"lng_context_unpin_from_top" = "Unpin from top";
@@ -1415,6 +1421,33 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_admin_log_admin_pin_messages" = "Pin messages";
"lng_admin_log_admin_add_admins" = "Add new admins";
"lng_feed_name" = "Feed";
"lng_feed_show_next" = "Show Next";
"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_feed_no_messages" = "No messages in this feed yet";
"lng_feed_channels#one" = "{count} channel";
"lng_feed_channels#other" = "{count} channels";
"lng_feed_notifications" = "Feed notifications";
"lng_feed_ungroup_all" = "Ungroup all channels";
"lng_feed_sure_ungroup_all" = "Are you sure you want to ungroup all channels from this feed?";
"lng_feed_ungroup_sure" = "Ungroup";
"lng_feed_create_new" = "New feed";
"lng_feed_too_few_channels#one" = "You need at least {count} channel to create a feed.";
"lng_feed_too_few_channels#other" = "You need at least {count} channels to create a feed.";
"lng_feed_select_more_channels#one" = "Select {count} channel or more.";
"lng_feed_select_more_channels#other" = "Select {count} channels or more.";
"lng_feed_create" = "Create";
"lng_feed_edit_title" = "Edit feed";
"lng_feed_channels_not_found" = "No channels found";
"lng_info_feed_title" = "Feed Info";
"lng_info_feed_is_default" = "Group new channels";
"lng_info_feed_channels" = "Channels";
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";

View File

@@ -155,16 +155,16 @@ inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile
inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile;
inputMediaEmpty#9664f57f = InputMedia;
inputMediaUploadedPhoto#2f37e231 flags:# file:InputFile caption:string stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
inputMediaPhoto#81fa373a flags:# id:InputPhoto caption:string ttl_seconds:flags.0?int = InputMedia;
inputMediaUploadedPhoto#1e287d04 flags:# file:InputFile stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
inputMediaPhoto#b3ba0635 flags:# id:InputPhoto ttl_seconds:flags.0?int = InputMedia;
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
inputMediaContact#a6e45987 phone_number:string first_name:string last_name:string = InputMedia;
inputMediaUploadedDocument#e39621fd flags:# nosound_video:flags.3?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
inputMediaDocument#5acb668e flags:# id:InputDocument caption:string ttl_seconds:flags.0?int = InputMedia;
inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
inputMediaDocument#23ab23d2 flags:# id:InputDocument ttl_seconds:flags.0?int = InputMedia;
inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia;
inputMediaGifExternal#4843b0fd url:string q:string = InputMedia;
inputMediaPhotoExternal#922aec1 flags:# url:string caption:string ttl_seconds:flags.0?int = InputMedia;
inputMediaDocumentExternal#b6f74335 flags:# url:string caption:string ttl_seconds:flags.0?int = InputMedia;
inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia;
inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia;
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
inputMediaGeoLive#7b1a118f geo_point:InputGeoPoint period:int = InputMedia;
@@ -240,11 +240,11 @@ message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:fl
messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
messageMediaPhoto#b5223b0f flags:# photo:flags.0?Photo caption:flags.1?string ttl_seconds:flags.2?int = MessageMedia;
messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
messageMediaContact#5e7d2f39 phone_number:string first_name:string last_name:string user_id:int = MessageMedia;
messageMediaUnsupported#9f84f49e = MessageMedia;
messageMediaDocument#7c4414d3 flags:# document:flags.0?Document caption:flags.1?string ttl_seconds:flags.2?int = MessageMedia;
messageMediaDocument#9cb070d7 flags:# document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia;
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia;
messageMediaGame#fdb19008 game:Game = MessageMedia;
@@ -270,6 +270,7 @@ messageActionPaymentSent#40699cd0 currency:string total_amount:long = MessageAct
messageActionPhoneCall#80e11a7f flags:# call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction;
messageActionScreenshotTaken#4792929b = MessageAction;
messageActionCustomAction#fae69f56 message:string = MessageAction;
messageActionBotAllowed#abe9affe domain:string = MessageAction;
dialog#e4def5db flags:# pinned:flags.2?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
@@ -422,8 +423,8 @@ updateRecentStickers#9a422c20 = Update;
updateConfig#a229dd06 = Update;
updatePtsChanged#3354678f = Update;
updateChannelWebPage#40771900 channel_id:int webpage:WebPage pts:int pts_count:int = Update;
updateDialogPinned#d711a2cc flags:# pinned:flags.0?true peer:Peer = Update;
updatePinnedDialogs#d8caf68d flags:# order:flags.0?Vector<Peer> = Update;
updateDialogPinned#19d27f3c flags:# pinned:flags.0?true peer:DialogPeer = Update;
updatePinnedDialogs#ea4cb65b flags:# order:flags.0?Vector<DialogPeer> = Update;
updateBotWebhookJSON#8317c0c3 data:DataJSON = Update;
updateBotWebhookJSONQuery#9b9240a6 query_id:long data:DataJSON timeout:int = Update;
updateBotShippingQuery#e0cdc940 query_id:long user_id:int payload:bytes shipping_address:PostAddress = Update;
@@ -457,11 +458,11 @@ photos.photosSlice#15051f54 count:int photos:Vector<Photo> users:Vector<User> =
photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;
upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
upload.fileCdnRedirect#ea52fe5a dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes cdn_file_hashes:Vector<CdnFileHash> = upload.File;
upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes file_hashes:Vector<FileHash> = upload.File;
dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int = DcOption;
config#9c840964 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string suggested_lang_code:flags.2?string lang_pack_version:flags.2?int disabled_features:Vector<DisabledFeature> = Config;
config#86b5778e flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string suggested_lang_code:flags.2?string lang_pack_version:flags.2?int = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
@@ -522,7 +523,7 @@ sendMessageGamePlayAction#dd6a8f48 = SendMessageAction;
sendMessageRecordRoundAction#88f27fbc = SendMessageAction;
sendMessageUploadRoundAction#243e1c66 progress:int = SendMessageAction;
contacts.found#1aa1f784 results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found;
contacts.found#b3134d9d my_results:Vector<Peer> results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found;
inputPrivacyKeyStatusTimestamp#4f96cb18 = InputPrivacyKey;
inputPrivacyKeyChatInvite#bdfb0426 = InputPrivacyKey;
@@ -553,7 +554,7 @@ accountDaysTTL#b8d0afdf days:int = AccountDaysTTL;
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker#6319d612 flags:# mask:flags.1?true alt:string stickerset:InputStickerSet mask_coords:flags.0?MaskCoords = DocumentAttribute;
documentAttributeVideo#ef02ce6 flags:# round_message:flags.0?true duration:int w:int h:int = DocumentAttribute;
documentAttributeVideo#ef02ce6 flags:# round_message:flags.0?true supports_streaming:flags.1?true duration:int w:int h:int = DocumentAttribute;
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
@@ -566,8 +567,6 @@ stickerPack#12b299d4 emoticon:string documents:Vector<long> = StickerPack;
messages.allStickersNotModified#e86602c3 = messages.AllStickers;
messages.allStickers#edfd405f hash:int sets:Vector<StickerSet> = messages.AllStickers;
disabledFeature#ae636f24 feature:string description:string = DisabledFeature;
messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMessages;
contactLinkUnknown#5f4f9247 = ContactLink;
@@ -605,7 +604,7 @@ inputStickerSetEmpty#ffb62b95 = InputStickerSet;
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
stickerSet#cd303b41 flags:# installed:flags.0?true archived:flags.1?true official:flags.2?true masks:flags.3?true id:long access_hash:long title:string short_name:string count:int hash:int = StickerSet;
stickerSet#5585a139 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string count:int hash:int = StickerSet;
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
@@ -642,6 +641,8 @@ messageEntityPre#73924be0 offset:int length:int language:string = MessageEntity;
messageEntityTextUrl#76a6d327 offset:int length:int url:string = MessageEntity;
messageEntityMentionName#352dca58 offset:int length:int user_id:int = MessageEntity;
inputMessageEntityMentionName#208e68c9 offset:int length:int user_id:InputUser = MessageEntity;
messageEntityPhone#9b69e34b offset:int length:int = MessageEntity;
messageEntityCashtag#4c4e743f offset:int length:int = MessageEntity;
inputChannelEmpty#ee8c1e86 = InputChannel;
inputChannel#afeb712e channel_id:int access_hash:long = InputChannel;
@@ -685,30 +686,30 @@ messages.foundGifs#450a1c0a next_offset:int results:Vector<FoundGif> = messages.
messages.savedGifsNotModified#e8025ca2 = messages.SavedGifs;
messages.savedGifs#2e0709a5 hash:int gifs:Vector<Document> = messages.SavedGifs;
inputBotInlineMessageMediaAuto#292fed13 flags:# caption:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaAuto#3380c786 flags:# message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaGeo#c1b15d65 flags:# geo_point:InputGeoPoint period:int reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaVenue#aaafadc8 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaContact#2daf01a7 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineResult#2cbbe15a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb_url:flags.4?string content_url:flags.5?string content_type:flags.5?string w:flags.6?int h:flags.6?int duration:flags.7?int send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultDocument#fff8fdc4 flags:# id:string type:string title:flags.1?string description:flags.2?string document:InputDocument send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:InputBotInlineMessage = InputBotInlineResult;
botInlineMessageMediaAuto#a74b15b flags:# caption:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaAuto#764cf810 flags:# message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaGeo#b722de65 flags:# geo:GeoPoint period:int reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaVenue#4366232e flags:# geo:GeoPoint title:string address:string provider:string venue_id:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaContact#35edb4d4 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineResult#9bebaeb9 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb_url:flags.4?string content_url:flags.5?string content_type:flags.5?string w:flags.6?int h:flags.6?int duration:flags.7?int send_message:BotInlineMessage = BotInlineResult;
botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult;
botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult;
messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string switch_pm:flags.2?InlineBotSwitchPM results:Vector<BotInlineResult> cache_time:int users:Vector<User> = messages.BotResults;
exportedMessageLink#1f486803 link:string = ExportedMessageLink;
exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink;
messageFwdHeader#559ebe6d flags:# from_id:flags.0?int date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int = MessageFwdHeader;
@@ -752,7 +753,7 @@ messages.featuredStickersNotModified#4ede3cf = messages.FeaturedStickers;
messages.featuredStickers#f89d88e5 hash:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
messages.recentStickersNotModified#b17f890 = messages.RecentStickers;
messages.recentStickers#5ce20970 hash:int stickers:Vector<Document> = messages.RecentStickers;
messages.recentStickers#22f3afb3 hash:int packs:Vector<StickerPack> stickers:Vector<Document> dates:Vector<int> = messages.RecentStickers;
messages.archivedStickers#4fcba9c8 count:int sets:Vector<StickerSetCovered> = messages.ArchivedStickers;
@@ -834,6 +835,7 @@ paymentRequestedInfo#909c3f94 flags:# name:flags.0?string phone:flags.1?string e
paymentSavedCredentialsCard#cdc27a1f id:string title:string = PaymentSavedCredentials;
webDocument#c61acbd8 url:string access_hash:long size:int mime_type:string attributes:Vector<DocumentAttribute> dc_id:int = WebDocument;
webDocumentNoProxy#f9c8bcc6 url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;
inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = InputWebDocument;
@@ -855,7 +857,7 @@ payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_inf
inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials;
inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials;
inputPaymentCredentialsApplePay#aa1c39f payment_data:DataJSON = InputPaymentCredentials;
inputPaymentCredentialsAndroidPay#795667a6 payment_token:DataJSON = InputPaymentCredentials;
inputPaymentCredentialsAndroidPay#ca05d50e payment_token:DataJSON google_transaction_id:string = InputPaymentCredentials;
account.tmpPassword#db64fd34 tmp_password:bytes valid_until:int = account.TmpPassword;
@@ -893,7 +895,7 @@ langPackDifference#f385c1f6 lang_code:string from_version:int version:int string
langPackLanguage#117698f1 name:string native_name:string lang_code:string = LangPackLanguage;
channelAdminRights#5d7ceba5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true invite_link:flags.6?true pin_messages:flags.7?true add_admins:flags.9?true = ChannelAdminRights;
channelAdminRights#5d7ceba5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true invite_link:flags.6?true pin_messages:flags.7?true add_admins:flags.9?true manage_call:flags.10?true = ChannelAdminRights;
channelBannedRights#58cf4249 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true until_date:int = ChannelBannedRights;
@@ -922,8 +924,6 @@ channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?tru
popularContact#5ce14175 client_id:long importers:int = PopularContact;
cdnFileHash#77eec38f offset:int limit:int hash:bytes = CdnFileHash;
messages.favedStickersNotModified#9e8fa6d3 = messages.FavedStickers;
messages.favedStickers#f37f2f16 hash:int packs:Vector<StickerPack> stickers:Vector<Document> = messages.FavedStickers;
@@ -935,7 +935,24 @@ recentMeUrlStickerSet#bc0a57dc url:string set:StickerSetCovered = RecentMeUrl;
help.recentMeUrls#e0310d7 urls:Vector<RecentMeUrl> chats:Vector<Chat> users:Vector<User> = help.RecentMeUrls;
inputSingleMedia#5eaa7809 media:InputMedia random_id:long = InputSingleMedia;
inputSingleMedia#1cc6e91f flags:# media:InputMedia random_id:long message:string entities:flags.0?Vector<MessageEntity> = InputSingleMedia;
webAuthorization#cac943f2 hash:long bot_id:int domain:string browser:string platform:string date_created:int date_active:int ip:string region:string = WebAuthorization;
account.webAuthorizations#ed56c9fc authorizations:Vector<WebAuthorization> users:Vector<User> = account.WebAuthorizations;
inputMessageID#a676a322 id:int = InputMessage;
inputMessageReplyTo#bad88395 id:int = InputMessage;
inputMessagePinned#86872538 = InputMessage;
inputDialogPeer#fcaafeb7 peer:InputPeer = InputDialogPeer;
dialogPeer#e56dbf05 peer:Peer = DialogPeer;
messages.foundStickerSetsNotModified#d54b65d = messages.FoundStickerSets;
messages.foundStickerSets#5108d648 hash:int sets:Vector<StickerSetCovered> = messages.FoundStickerSets;
fileHash#6242c773 offset:int limit:int hash:bytes = FileHash;
---functions---
@@ -963,8 +980,8 @@ auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentC
auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool;
auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector<long> = Bool;
account.registerDevice#637ea878 token_type:int token:string = Bool;
account.unregisterDevice#65c55b40 token_type:int token:string = Bool;
account.registerDevice#5cbea590 token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector<int> = Bool;
account.unregisterDevice#3076c4bf token_type:int token:string other_uids:Vector<int> = Bool;
account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool;
account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings;
account.resetNotifySettings#db7e1747 = Bool;
@@ -990,6 +1007,9 @@ account.updatePasswordSettings#fa7c4b86 current_password_hash:bytes new_settings
account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode;
account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool;
account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPassword;
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
account.resetWebAuthorization#2d01b9ef hash:long = Bool;
account.resetWebAuthorizations#682d2594 = Bool;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
@@ -1010,21 +1030,22 @@ contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.
contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool;
contacts.resetSaved#879537f1 = Bool;
messages.getMessages#4222fa74 id:Vector<int> = messages.Messages;
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
messages.getDialogs#191ba9c5 flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int = messages.Dialogs;
messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
messages.search#39e9ea0 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.search#8614ef68 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages;
messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true peer:InputPeer max_id:int = messages.AffectedHistory;
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.sendMedia#c8f16791 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia random_id:long reply_markup:flags.2?ReplyMarkup = Updates;
messages.sendMedia#b8d1262b flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.hideReportSpam#a8f1709b peer:InputPeer = Bool;
messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings;
messages.report#bd82b658 peer:InputPeer id:Vector<int> reason:ReportReason = Bool;
messages.getChats#3c6aa187 id:Vector<int> = messages.Chats;
messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull;
messages.editChatTitle#dc452855 chat_id:int title:string = Updates;
@@ -1032,7 +1053,6 @@ messages.editChatPhoto#ca4c79d8 chat_id:int photo:InputChatPhoto = Updates;
messages.addChatUser#f9a0aa09 chat_id:int user_id:InputUser fwd_limit:int = Updates;
messages.deleteChatUser#e0611f16 chat_id:int user_id:InputUser = Updates;
messages.createChat#9cb126e users:Vector<InputUser> title:string = Updates;
messages.forwardMessage#33963bf9 peer:InputPeer id:int random_id:long = Updates;
messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig;
messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat;
messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat;
@@ -1045,8 +1065,9 @@ messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long da
messages.receivedQueue#55a5bb66 max_qts:int = Vector<long>;
messages.reportEncryptedSpam#4b0c8c0f peer:InputEncryptedChat = Bool;
messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages;
messages.getStickers#85cb5182 flags:# exclude_featured:flags.0?true emoticon:string hash:string = messages.Stickers;
messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers;
messages.getWebPagePreview#25223e24 message:string = MessageMedia;
messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;
messages.exportChatInvite#7d885289 chat_id:int = ExportedChatInvite;
messages.checkChatInvite#3eadb1bb hash:string = ChatInvite;
messages.importChatInvite#6c50051c hash:string = Updates;
@@ -1072,7 +1093,7 @@ messages.editMessage#5d1b8dd flags:# no_webpage:flags.1?true stop_geo_live:flags
messages.editInlineBotMessage#b0e08243 flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true id:InputBotInlineMessageID message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> geo_point:flags.13?InputGeoPoint = Bool;
messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer;
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
messages.getPeerDialogs#2d9776b9 peers:Vector<InputPeer> = messages.PeerDialogs;
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
messages.getAllDrafts#6a3f8d65 = Updates;
messages.getFeaturedStickers#2dacca4f hash:int = messages.FeaturedStickers;
@@ -1090,8 +1111,8 @@ messages.getInlineGameHighScores#f635e1b id:InputBotInlineMessageID user_id:Inpu
messages.getCommonChats#d0a48c4 user_id:InputUser max_id:int limit:int = messages.Chats;
messages.getAllChats#eba80ff0 except_ids:Vector<int> = messages.Chats;
messages.getWebPage#32ca8f91 url:string hash:int = WebPage;
messages.toggleDialogPin#3289be6a flags:# pinned:flags.0?true peer:InputPeer = Bool;
messages.reorderPinnedDialogs#959ff644 flags:# force:flags.0?true order:Vector<InputPeer> = Bool;
messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
messages.reorderPinnedDialogs#5b51d63f flags:# force:flags.0?true order:Vector<InputDialogPeer> = Bool;
messages.getPinnedDialogs#e254d64e = messages.PeerDialogs;
messages.setBotShippingResults#e5f672fa flags:# query_id:long error:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = Bool;
messages.setBotPrecheckoutResults#9c2dd95 flags:# success:flags.1?true query_id:long error:flags.0?string = Bool;
@@ -1101,9 +1122,10 @@ messages.getFavedStickers#21ce0b0e hash:int = messages.FavedStickers;
messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
messages.getRecentLocations#249431e2 peer:InputPeer limit:int = messages.Messages;
messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = messages.Messages;
messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> = Updates;
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@@ -1119,8 +1141,9 @@ upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload
upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile;
upload.reuploadCdnFile#1af91c09 file_token:bytes request_token:bytes = Vector<CdnFileHash>;
upload.getCdnFileHashes#f715c87b file_token:bytes offset:int = Vector<CdnFileHash>;
upload.reuploadCdnFile#9b2754a8 file_token:bytes request_token:bytes = Vector<FileHash>;
upload.getCdnFileHashes#4da54231 file_token:bytes offset:int = Vector<FileHash>;
upload.getFileHashes#c7025931 location:InputFileLocation offset:int = Vector<FileHash>;
help.getConfig#c4f9186b = Config;
help.getNearestDc#1fb33026 = NearestDc;
@@ -1138,7 +1161,7 @@ channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
channels.deleteUserHistory#d10dd71b channel:InputChannel user_id:InputUser = messages.AffectedHistory;
channels.reportSpam#fe087810 channel:InputChannel user_id:InputUser id:Vector<int> = Bool;
channels.getMessages#93d7b347 channel:InputChannel id:Vector<int> = messages.Messages;
channels.getMessages#ad8c9a23 channel:InputChannel id:Vector<InputMessage> = messages.Messages;
channels.getParticipants#123e05e9 channel:InputChannel filter:ChannelParticipantsFilter offset:int limit:int hash:int = channels.ChannelParticipants;
channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channels.ChannelParticipant;
channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats;
@@ -1156,7 +1179,7 @@ channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> =
channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite;
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.toggleInvites#49609307 channel:InputChannel enabled:Bool = Updates;
channels.exportMessageLink#c846d22d channel:InputChannel id:int = ExportedMessageLink;
channels.exportMessageLink#ceb77163 channel:InputChannel id:int grouped:Bool = ExportedMessageLink;
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates;
channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats;
@@ -1196,4 +1219,4 @@ langpack.getStrings#2e1ee318 lang_code:string keys:Vector<string> = Vector<LangP
langpack.getDifference#b2e4d7d from_version:int = LangPackDifference;
langpack.getLanguages#800fd57d = Vector<LangPackLanguage>;
// LAYER 73
// LAYER 76

View File

@@ -9,7 +9,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="1.2.8.0" />
Version="1.2.9.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View File

@@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,2,8,0
PRODUCTVERSION 1,2,8,0
FILEVERSION 1,2,9,0
PRODUCTVERSION 1,2,9,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -52,10 +52,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "1.2.8.0"
VALUE "FileVersion", "1.2.9.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2018"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.2.8.0"
VALUE "ProductVersion", "1.2.9.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,2,8,0
PRODUCTVERSION 1,2,8,0
FILEVERSION 1,2,9,0
PRODUCTVERSION 1,2,9,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -43,10 +43,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "1.2.8.0"
VALUE "FileVersion", "1.2.9.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2018"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.2.8.0"
VALUE "ProductVersion", "1.2.9.0"
END
END
BLOCK "VarFileInfo"

File diff suppressed because it is too large Load Diff

View File

@@ -9,15 +9,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/event_stream.h>
#include "base/timer.h"
#include "core/single_timer.h"
#include "mtproto/sender.h"
#include "base/flat_map.h"
#include "base/flat_set.h"
#include "core/single_timer.h"
#include "mtproto/sender.h"
#include "chat_helpers/stickers.h"
#include "data/data_messages.h"
class TaskQueue;
class AuthSession;
enum class SparseIdsLoadDirection;
struct MessageGroupId;
struct SendingAlbum;
enum class SendMediaType;
@@ -27,6 +27,10 @@ enum class SharedMediaType : char;
struct PreparedList;
} // namespace Storage
namespace Dialogs {
class Key;
} // namespace Dialogs
namespace Api {
inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Chats &chats) {
@@ -37,6 +41,16 @@ inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Cha
return nullptr;
}
template <typename IntRange>
inline int32 CountHash(IntRange &&range) {
uint32 acc = 0;
for (auto value : range) {
acc += (acc * 20261) + uint32(value);
}
return int32(acc & 0x7FFFFFFF);
}
} // namespace Api
class ApiWrap : private MTP::Sender, private base::Subscriber {
@@ -45,8 +59,27 @@ public:
void applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId = 0);
void savePinnedOrder();
//void toggleChannelGrouping( // #feed
// not_null<ChannelData*> channel,
// bool group,
// base::lambda<void()> callback);
//void ungroupAllFromFeed(not_null<Data::Feed*> feed);
using RequestMessageDataCallback = base::lambda<void(ChannelData*, MsgId)>;
void requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback callback);
void requestMessageData(
ChannelData *channel,
MsgId msgId,
RequestMessageDataCallback callback);
void requestContacts();
void requestDialogEntry(not_null<Data::Feed*> feed);
//void requestFeedDialogsEntries(not_null<Data::Feed*> feed);
void requestDialogEntry(not_null<History*> history);
//void applyFeedSources(const MTPDchannels_feedSources &data); // #feed
//void setFeedChannels(
// not_null<Data::Feed*> feed,
// const std::vector<not_null<ChannelData*>> &channels);
void requestFullPeer(PeerData *peer);
void requestPeer(PeerData *peer);
@@ -55,6 +88,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,
@@ -66,6 +100,9 @@ public:
void processFullPeer(PeerData *peer, const MTPmessages_ChatFull &result);
void processFullPeer(UserData *user, const MTPUserFull &result);
void markMediaRead(const base::flat_set<not_null<HistoryItem*>> &items);
void markMediaRead(not_null<HistoryItem*> item);
void requestSelfParticipant(ChannelData *channel);
void kickParticipant(not_null<ChatData*> chat, not_null<UserData*> user);
void kickParticipant(
@@ -75,6 +112,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);
@@ -82,9 +122,16 @@ public:
void scheduleStickerSetRequest(uint64 setId, uint64 access);
void requestStickerSets();
void saveStickerSets(const Stickers::Order &localOrder, const Stickers::Order &localRemoved);
void saveStickerSets(
const Stickers::Order &localOrder,
const Stickers::Order &localRemoved);
void updateStickers();
void setGroupStickerSet(not_null<ChannelData*> megagroup, const MTPInputStickerSet &set);
void requestRecentStickersForce();
void setGroupStickerSet(
not_null<ChannelData*> megagroup,
const MTPInputStickerSet &set);
std::vector<not_null<DocumentData*>> *stickersByEmoji(
not_null<EmojiPtr> emoji);
void joinChannel(ChannelData *channel);
void leaveChannel(ChannelData *channel);
@@ -112,7 +159,7 @@ public:
void applyUpdatesNoPtsCheck(const MTPUpdates &updates);
void applyUpdateNoPtsCheck(const MTPUpdate &update);
void jumpToDate(not_null<PeerData*> peer, const QDate &date);
void jumpToDate(Dialogs::Key chat, const QDate &date);
void preloadEnoughUnreadMentions(not_null<History*> history);
void checkForUnreadMentions(const base::flat_set<MsgId> &possiblyReadMentions, ChannelData *channel = nullptr);
@@ -122,7 +169,7 @@ public:
bool adminsEnabled,
base::flat_set<not_null<UserData*>> &&admins);
using SliceType = SparseIdsLoadDirection;
using SliceType = Data::LoadDirection;
void requestSharedMedia(
not_null<PeerData*> peer,
Storage::SharedMediaType type,
@@ -136,6 +183,14 @@ public:
not_null<UserData*> user,
PhotoId afterId);
//void requestFeedChannels( // #feed
// not_null<Data::Feed*> feed);
//void requestFeedMessages(
// not_null<Data::Feed*> feed,
// Data::MessagePosition messageId,
// SliceType slice);
//void saveDefaultFeedId(FeedId id, bool isDefaultFeedId);
void stickerSetInstalled(uint64 setId) {
_stickerSetInstalled.fire_copy(setId);
}
@@ -185,6 +240,9 @@ public:
void shareContact(not_null<UserData*> user, const SendOptions &options);
void readServerHistory(not_null<History*> history);
void readServerHistoryForce(not_null<History*> history);
void readFeed(
not_null<Data::Feed*> feed,
Data::MessagePosition position);
void sendVoiceMessage(
QByteArray result,
@@ -224,6 +282,12 @@ private:
using MessageDataRequests = QMap<MsgId, MessageDataRequest>;
using SharedMediaType = Storage::SharedMediaType;
struct StickersByEmoji {
std::vector<not_null<DocumentData*>> list;
QString hash;
TimeMs received = 0;
};
void updatesReceived(const MTPUpdates &updates);
void checkQuitPreventFinished();
@@ -235,8 +299,13 @@ private:
ChannelData *channel,
mtpRequestId requestId);
QVector<MTPint> collectMessageIds(const MessageDataRequests &requests);
QVector<MTPInputMessage> collectMessageIds(const MessageDataRequests &requests);
MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false);
void applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs);
void historyDialogEntryApplied(not_null<History*> history);
void applyFeedDialogs(
not_null<Data::Feed*> feed,
const MTPmessages_Dialogs &dialogs);
void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req);
void gotUserFull(UserData *user, const MTPUserFull &result, mtpRequestId req);
@@ -253,16 +322,31 @@ 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();
void requestStickers(TimeId now);
void requestRecentStickers(TimeId now);
void requestRecentStickersWithHash(int32 hash);
void requestFavedStickers(TimeId now);
void requestFeaturedStickers(TimeId now);
void requestSavedGifs(TimeId now);
@@ -275,11 +359,19 @@ private:
not_null<ChannelData*> channel,
const QVector<MTPChannelParticipant> &participants);
void jumpToHistoryDate(not_null<PeerData*> peer, const QDate &date);
void jumpToFeedDate(not_null<Data::Feed*> feed, const QDate &date);
template <typename Callback>
void requestMessageAfterDate(
not_null<PeerData*> peer,
const QDate &date,
Callback &&callback);
template <typename Callback>
void requestMessageAfterDate(
not_null<Data::Feed*> feed,
const QDate &date,
Callback &&callback);
void sharedMediaDone(
not_null<PeerData*> peer,
@@ -293,6 +385,13 @@ private:
PhotoId photoId,
const MTPphotos_Photos &result);
//void feedChannelsDone(not_null<Data::Feed*> feed); // #feed
//void feedMessagesDone(
// not_null<Data::Feed*> feed,
// Data::MessagePosition messageId,
// SliceType slice,
// const MTPmessages_FeedMessages &result);
void sendSharedContact(
const QString &phone,
const QString &firstName,
@@ -308,6 +407,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,
@@ -331,6 +435,8 @@ private:
bool silent,
uint64 randomId);
void readFeeds();
not_null<AuthSession*> _session;
MessageDataRequests _messageDataRequests;
@@ -348,7 +454,11 @@ private:
ChannelData *_channelMembersForAdd = nullptr;
mtpRequestId _channelMembersForAddRequestId = 0;
base::lambda<void(const MTPchannels_ChannelParticipants&)> _channelMembersForAddCallback;
base::lambda<void(
const MTPchannels_ChannelParticipants&)> _channelMembersForAddCallback;
base::flat_map<
not_null<ChannelData*>,
std::pair<mtpRequestId,base::lambda<void()>>> _channelGroupingRequests;
using KickRequest = std::pair<
not_null<ChannelData*>,
@@ -357,6 +467,10 @@ private:
QMap<ChannelData*, mtpRequestId> _selfParticipantRequests;
base::flat_map<
not_null<ChannelData*>,
mtpRequestId> _rangeDifferenceRequests;
QMap<WebPageData*, mtpRequestId> _webPagesPending;
base::Timer _webPagesTimer;
@@ -385,9 +499,14 @@ private:
base::Timer _featuredSetsReadTimer;
base::flat_set<uint64> _featuredSetsRead;
QMap<mtpTypeId, mtpRequestId> _privacySaveRequests;
base::flat_map<not_null<EmojiPtr>, StickersByEmoji> _stickersByEmoji;
base::flat_map<mtpTypeId, mtpRequestId> _privacySaveRequests;
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;
@@ -409,6 +528,20 @@ private:
base::flat_map<not_null<UserData*>, mtpRequestId> _userPhotosRequests;
base::flat_set<not_null<Data::Feed*>> _feedChannelsGetRequests;
base::flat_map<
not_null<Data::Feed*>,
mtpRequestId> _feedChannelsSetRequests;
base::flat_set<std::tuple<
not_null<Data::Feed*>,
Data::MessagePosition,
SliceType>> _feedMessagesRequests;
base::flat_set<std::tuple<
not_null<Data::Feed*>,
Data::MessagePosition,
SliceType>> _feedMessagesRequestsPending;
mtpRequestId _saveDefaultFeedIdRequest = 0;
rpl::event_stream<SendOptions> _sendActions;
struct ReadRequest {
@@ -422,6 +555,7 @@ private:
};
base::flat_map<not_null<PeerData*>, ReadRequest> _readRequests;
base::flat_map<not_null<PeerData*>, MsgId> _readRequestsPending;
std::unique_ptr<TaskQueue> _fileLoader;
base::flat_map<uint64, std::shared_ptr<SendingAlbum>> _sendingAlbums;
@@ -429,4 +563,8 @@ private:
rpl::event_stream<uint64> _stickerSetInstalled;
base::flat_map<not_null<Data::Feed*>, TimeMs> _feedReadsDelayed;
base::flat_map<not_null<Data::Feed*>, mtpRequestId> _feedReadRequests;
base::Timer _feedReadTimer;
};

File diff suppressed because it is too large Load Diff

View File

@@ -8,27 +8,60 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "core/basic_types.h"
#include "history/history.h"
#include "history/history_item.h"
#include "layout.h"
#include "data/data_types.h"
#include "data/data_peer.h"
enum NewMessageType : char;
class Messenger;
class MainWindow;
class MainWidget;
using HistoryItemsMap = OrderedSet<HistoryItem*>;
using PhotoItems = QHash<PhotoData*, HistoryItemsMap>;
using DocumentItems = QHash<DocumentData*, HistoryItemsMap>;
using WebPageItems = QHash<WebPageData*, HistoryItemsMap>;
using GameItems = QHash<GameData*, HistoryItemsMap>;
using SharedContactItems = QHash<int32, HistoryItemsMap>;
using GifItems = QHash<Media::Clip::Reader*, HistoryItem*>;
using PhotosData = QHash<PhotoId, PhotoData*>;
using DocumentsData = QHash<DocumentId, DocumentData*>;
class LocationCoords;
struct LocationData;
class HistoryItem;
class History;
class Histories;
namespace HistoryView {
class Element;
} // namespace HistoryView
using HistoryItemsMap = base::flat_set<not_null<HistoryItem*>>;
using GifItems = QHash<Media::Clip::Reader*, HistoryItem*>;
enum RoundCorners {
SmallMaskCorners = 0x00, // for images
LargeMaskCorners,
BoxCorners,
MenuCorners,
BotKbOverCorners,
StickerCorners,
StickerSelectedCorners,
SelectedOverlaySmallCorners,
SelectedOverlayLargeCorners,
DateCorners,
DateSelectedCorners,
ForwardCorners,
MediaviewSaveCorners,
EmojiHoverCorners,
StickerHoverCorners,
BotKeyboardCorners,
PhotoSelectOverlayCorners,
Doc1Corners,
Doc2Corners,
Doc3Corners,
Doc4Corners,
InShadowCorners, // for photos without bg
InSelectedShadowCorners,
MessageInCorners, // with shadow
MessageInSelectedCorners,
MessageOutCorners,
MessageOutSelectedCorners,
RoundCornersCount
};
namespace App {
MainWindow *wnd();
@@ -62,18 +95,6 @@ namespace App {
ImagePtr image(const MTPPhotoSize &size);
PhotoData *feedPhoto(const MTPPhoto &photo, const PreparedPhotoThumbs &thumbs);
PhotoData *feedPhoto(const MTPPhoto &photo, PhotoData *convert = nullptr);
PhotoData *feedPhoto(const MTPDphoto &photo, PhotoData *convert = nullptr);
DocumentData *feedDocument(const MTPdocument &document, const QPixmap &thumb);
DocumentData *feedDocument(const MTPdocument &document, DocumentData *convert = nullptr);
DocumentData *feedDocument(const MTPDdocument &document, DocumentData *convert = nullptr);
WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert = nullptr);
WebPageData *feedWebPage(const MTPDwebPagePending &webpage, WebPageData *convert = nullptr);
WebPageData *feedWebPage(const MTPWebPage &webpage);
WebPageData *feedWebPage(WebPageId webPageId, const QString &siteName, const TextWithEntities &content);
GameData *feedGame(const MTPDgame &game, GameData *convert = nullptr);
PeerData *peer(const PeerId &id, PeerData::LoadedStatus restriction = PeerData::NotLoaded);
inline UserData *user(const PeerId &id, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
return asUser(peer(id, restriction));
@@ -119,53 +140,7 @@ namespace App {
UserData *self();
PeerData *peerByName(const QString &username);
QString peerName(const PeerData *peer, bool forDialogs = false);
PhotoData *photo(const PhotoId &photo);
PhotoData *photoSet(
const PhotoId &photo,
PhotoData *convert,
const uint64 &access,
int32 date,
const ImagePtr &thumb,
const ImagePtr &medium,
const ImagePtr &full);
DocumentData *document(const DocumentId &document);
DocumentData *documentSet(
const DocumentId &document,
DocumentData *convert,
const uint64 &access,
int32 version,
int32 date,
const QVector<MTPDocumentAttribute> &attributes,
const QString &mime,
const ImagePtr &thumb,
int32 dc,
int32 size,
const StorageImageLocation &thumbLocation);
WebPageData *webPage(const WebPageId &webPage);
WebPageData *webPageSet(
const WebPageId &webPage,
WebPageData *convert,
const QString &type,
const QString &url,
const QString &displayUrl,
const QString &siteName,
const QString &title,
const TextWithEntities &description,
PhotoData *photo,
DocumentData *document,
int duration,
const QString &author,
int pendingTill);
GameData *game(const GameId &game);
GameData *gameSet(
const GameId &game,
GameData *convert,
const uint64 &accessHash,
const QString &shortName,
const QString &title,
const QString &description,
PhotoData *photo,
DocumentData *document);
LocationData *location(const LocationCoords &coords);
void forgetMedia();
@@ -173,7 +148,6 @@ namespace App {
Histories &histories();
not_null<History*> history(const PeerId &peer);
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) {
@@ -189,10 +163,9 @@ namespace App {
inline HistoryItem *histItemById(const FullMsgId &msgId) {
return histItemById(msgId.channel, msgId.msg);
}
void historyRegItem(HistoryItem *item);
void historyItemDetached(HistoryItem *item);
void historyUnregItem(HistoryItem *item);
void historyUpdateDependent(HistoryItem *item);
void historyRegItem(not_null<HistoryItem*> item);
void historyUnregItem(not_null<HistoryItem*> item);
void historyUpdateDependent(not_null<HistoryItem*> item);
void historyClearMsgs();
void historyClearItems();
void historyRegDependency(HistoryItem *dependent, HistoryItem *dependency);
@@ -205,18 +178,16 @@ namespace App {
void historyUnregSentData(uint64 randomId);
void histSentDataByItem(uint64 randomId, PeerId &peerId, QString &text);
void hoveredItem(HistoryItem *item);
HistoryItem *hoveredItem();
void pressedItem(HistoryItem *item);
HistoryItem *pressedItem();
void hoveredLinkItem(HistoryItem *item);
HistoryItem *hoveredLinkItem();
void pressedLinkItem(HistoryItem *item);
HistoryItem *pressedLinkItem();
void contextItem(HistoryItem *item);
HistoryItem *contextItem();
void mousedItem(HistoryItem *item);
HistoryItem *mousedItem();
void hoveredItem(HistoryView::Element *item);
HistoryView::Element *hoveredItem();
void pressedItem(HistoryView::Element *item);
HistoryView::Element *pressedItem();
void hoveredLinkItem(HistoryView::Element *item);
HistoryView::Element *hoveredLinkItem();
void pressedLinkItem(HistoryView::Element *item);
HistoryView::Element *pressedLinkItem();
void mousedItem(HistoryView::Element *item);
HistoryView::Element *mousedItem();
void clearMousedItems();
const style::font &monofont();
@@ -250,34 +221,6 @@ namespace App {
QImage readImage(const QString &file, QByteArray *format = nullptr, bool opaque = true, bool *animated = nullptr, QByteArray *content = 0);
QPixmap pixmapFromImageInPlace(QImage &&image);
void regPhotoItem(PhotoData *data, HistoryItem *item);
void unregPhotoItem(PhotoData *data, HistoryItem *item);
const PhotoItems &photoItems();
const PhotosData &photosData();
void regDocumentItem(DocumentData *data, HistoryItem *item);
void unregDocumentItem(DocumentData *data, HistoryItem *item);
const DocumentItems &documentItems();
const DocumentsData &documentsData();
void regWebPageItem(WebPageData *data, HistoryItem *item);
void unregWebPageItem(WebPageData *data, HistoryItem *item);
const WebPageItems &webPageItems();
void regGameItem(GameData *data, HistoryItem *item);
void unregGameItem(GameData *data, HistoryItem *item);
const GameItems &gameItems();
void regSharedContactItem(int32 userId, HistoryItem *item);
void unregSharedContactItem(int32 userId, HistoryItem *item);
const SharedContactItems &sharedContactItems();
QString phoneFromSharedContact(int32 userId);
void regGifItem(Media::Clip::Reader *reader, HistoryItem *item);
void unregGifItem(Media::Clip::Reader *reader);
void stopRoundVideoPlayback();
void stopGifItems();
void regMuted(not_null<PeerData*> peer, TimeMs changeIn);
void unregMuted(not_null<PeerData*> peer);
void updateMuted();

View File

@@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h"
#include "storage/storage_facade.h"
#include "storage/serialize_common.h"
#include "history/history_item_components.h"
#include "data/data_session.h"
#include "window/notifications_manager.h"
#include "window/themes/window_theme.h"
#include "platform/platform_specific.h"
@@ -30,14 +30,14 @@ constexpr auto kAutoLockTimeoutLateMs = TimeMs(3000);
} // namespace
AuthSessionData::Variables::Variables()
AuthSessionSettings::Variables::Variables()
: sendFilesWay(SendFilesWay::Album)
, selectorTab(ChatHelpers::SelectorTab::Emoji)
, floatPlayerColumn(Window::Column::Second)
, floatPlayerCorner(RectPart::TopRight) {
}
QByteArray AuthSessionData::serialize() const {
QByteArray AuthSessionSettings::serialize() const {
auto size = sizeof(qint32) * 10;
for (auto i = _variables.soundOverrides.cbegin(), e = _variables.soundOverrides.cend(); i != e; ++i) {
size += Serialize::stringSize(i.key()) + Serialize::stringSize(i.value());
@@ -76,7 +76,7 @@ QByteArray AuthSessionData::serialize() const {
return result;
}
void AuthSessionData::constructFromSerialized(const QByteArray &serialized) {
void AuthSessionSettings::constructFromSerialized(const QByteArray &serialized) {
if (serialized.isEmpty()) {
return;
}
@@ -146,7 +146,8 @@ void AuthSessionData::constructFromSerialized(const QByteArray &serialized) {
thirdSectionExtendedBy = value;
}
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: Bad data for AuthSessionData::constructFromSerialized()"));
LOG(("App Error: "
"Bad data for AuthSessionSettings::constructFromSerialized()"));
return;
}
@@ -190,87 +191,7 @@ void AuthSessionData::constructFromSerialized(const QByteArray &serialized) {
}
}
void AuthSessionData::markItemLayoutChanged(not_null<const HistoryItem*> item) {
_itemLayoutChanged.fire_copy(item);
}
rpl::producer<not_null<const HistoryItem*>> AuthSessionData::itemLayoutChanged() const {
return _itemLayoutChanged.events();
}
void AuthSessionData::requestItemRepaint(not_null<const HistoryItem*> item) {
_itemRepaintRequest.fire_copy(item);
}
rpl::producer<not_null<const HistoryItem*>> AuthSessionData::itemRepaintRequest() const {
return _itemRepaintRequest.events();
}
void AuthSessionData::markItemRemoved(not_null<const HistoryItem*> item) {
_itemRemoved.fire_copy(item);
}
rpl::producer<not_null<const HistoryItem*>> AuthSessionData::itemRemoved() const {
return _itemRemoved.events();
}
void AuthSessionData::markHistoryUnloaded(not_null<const History*> history) {
_historyUnloaded.fire_copy(history);
}
rpl::producer<not_null<const History*>> AuthSessionData::historyUnloaded() const {
return _historyUnloaded.events();
}
void AuthSessionData::markHistoryCleared(not_null<const History*> history) {
_historyCleared.fire_copy(history);
}
rpl::producer<not_null<const History*>> AuthSessionData::historyCleared() const {
return _historyCleared.events();
}
void AuthSessionData::removeMegagroupParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user) {
_megagroupParticipantRemoved.fire({ channel, user });
}
auto AuthSessionData::megagroupParticipantRemoved() const -> rpl::producer<MegagroupParticipant> {
return _megagroupParticipantRemoved.events();
}
rpl::producer<not_null<UserData*>> AuthSessionData::megagroupParticipantRemoved(
not_null<ChannelData*> channel) const {
return megagroupParticipantRemoved(
) | rpl::filter([channel](auto updateChannel, auto user) {
return (updateChannel == channel);
}) | rpl::map([](auto updateChannel, auto user) {
return user;
});
}
void AuthSessionData::addNewMegagroupParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user) {
_megagroupParticipantAdded.fire({ channel, user });
}
auto AuthSessionData::megagroupParticipantAdded() const -> rpl::producer<MegagroupParticipant> {
return _megagroupParticipantAdded.events();
}
rpl::producer<not_null<UserData*>> AuthSessionData::megagroupParticipantAdded(
not_null<ChannelData*> channel) const {
return megagroupParticipantAdded(
) | rpl::filter([channel](auto updateChannel, auto user) {
return (updateChannel == channel);
}) | rpl::map([](auto updateChannel, auto user) {
return user;
});
}
void AuthSessionData::setTabbedSelectorSectionEnabled(bool enabled) {
void AuthSessionSettings::setTabbedSelectorSectionEnabled(bool enabled) {
_variables.tabbedSelectorSectionEnabled = enabled;
if (enabled) {
setThirdSectionInfoEnabled(false);
@@ -278,12 +199,12 @@ void AuthSessionData::setTabbedSelectorSectionEnabled(bool enabled) {
setTabbedReplacedWithInfo(false);
}
rpl::producer<bool> AuthSessionData::tabbedReplacedWithInfoValue() const {
rpl::producer<bool> AuthSessionSettings::tabbedReplacedWithInfoValue() const {
return _tabbedReplacedWithInfoValue.events_starting_with(
tabbedReplacedWithInfo());
}
void AuthSessionData::setThirdSectionInfoEnabled(bool enabled) {
void AuthSessionSettings::setThirdSectionInfoEnabled(bool enabled) {
if (_variables.thirdSectionInfoEnabled != enabled) {
_variables.thirdSectionInfoEnabled = enabled;
if (enabled) {
@@ -294,19 +215,19 @@ void AuthSessionData::setThirdSectionInfoEnabled(bool enabled) {
}
}
rpl::producer<bool> AuthSessionData::thirdSectionInfoEnabledValue() const {
rpl::producer<bool> AuthSessionSettings::thirdSectionInfoEnabledValue() const {
return _thirdSectionInfoEnabledValue.events_starting_with(
thirdSectionInfoEnabled());
}
void AuthSessionData::setTabbedReplacedWithInfo(bool enabled) {
void AuthSessionSettings::setTabbedReplacedWithInfo(bool enabled) {
if (_tabbedReplacedWithInfo != enabled) {
_tabbedReplacedWithInfo = enabled;
_tabbedReplacedWithInfoValue.fire_copy(enabled);
}
}
QString AuthSessionData::getSoundPath(const QString &key) const {
QString AuthSessionSettings::getSoundPath(const QString &key) const {
auto it = _variables.soundOverrides.constFind(key);
if (it != _variables.soundOverrides.end()) {
return it.value();
@@ -314,75 +235,30 @@ QString AuthSessionData::getSoundPath(const QString &key) const {
return qsl(":/sounds/") + key + qsl(".mp3");
}
void AuthSessionData::setDialogsWidthRatio(float64 ratio) {
void AuthSessionSettings::setDialogsWidthRatio(float64 ratio) {
_variables.dialogsWidthRatio = ratio;
}
float64 AuthSessionData::dialogsWidthRatio() const {
float64 AuthSessionSettings::dialogsWidthRatio() const {
return _variables.dialogsWidthRatio.current();
}
rpl::producer<float64> AuthSessionData::dialogsWidthRatioChanges() const {
rpl::producer<float64> AuthSessionSettings::dialogsWidthRatioChanges() const {
return _variables.dialogsWidthRatio.changes();
}
void AuthSessionData::setThirdColumnWidth(int width) {
void AuthSessionSettings::setThirdColumnWidth(int width) {
_variables.thirdColumnWidth = width;
}
int AuthSessionData::thirdColumnWidth() const {
int AuthSessionSettings::thirdColumnWidth() const {
return _variables.thirdColumnWidth.current();
}
rpl::producer<int> AuthSessionData::thirdColumnWidthChanges() const {
rpl::producer<int> AuthSessionSettings::thirdColumnWidthChanges() const {
return _variables.thirdColumnWidth.changes();
}
void AuthSessionData::markStickersUpdated() {
_stickersUpdated.fire({});
}
rpl::producer<> AuthSessionData::stickersUpdated() const {
return _stickersUpdated.events();
}
void AuthSessionData::markSavedGifsUpdated() {
_savedGifsUpdated.fire({});
}
rpl::producer<> AuthSessionData::savedGifsUpdated() const {
return _savedGifsUpdated.events();
}
HistoryItemsList AuthSessionData::idsToItems(
const MessageIdsList &ids) const {
return ranges::view::all(
ids
) | ranges::view::transform([](const FullMsgId &fullId) {
return App::histItemById(fullId);
}) | ranges::view::filter([](HistoryItem *item) {
return item != nullptr;
}) | ranges::view::transform([](HistoryItem *item) {
return not_null<HistoryItem*>(item);
}) | ranges::to_vector;
}
MessageIdsList AuthSessionData::itemsToIds(
const HistoryItemsList &items) const {
return ranges::view::all(
items
) | ranges::view::transform([](not_null<HistoryItem*> item) {
return item->fullId();
}) | ranges::to_vector;
}
MessageIdsList AuthSessionData::groupToIds(
not_null<HistoryMessageGroup*> group) const {
auto result = itemsToIds(group->others);
result.push_back(group->leader->fullId());
return result;
}
AuthSession &Auth() {
auto result = Messenger::Instance().authSession();
Assert(result != nullptr);
@@ -398,6 +274,7 @@ AuthSession::AuthSession(UserId userId)
, _uploader(std::make_unique<Storage::Uploader>())
, _storage(std::make_unique<Storage::Facade>())
, _notifications(std::make_unique<Window::Notifications::System>(this))
, _data(std::make_unique<Data::Session>(this))
, _changelogs(Core::Changelogs::Create(this)) {
Expects(_userId != 0);
@@ -435,7 +312,7 @@ bool AuthSession::validateSelf(const MTPUser &user) {
return true;
}
void AuthSession::saveDataDelayed(TimeMs delay) {
void AuthSession::saveSettingsDelayed(TimeMs delay) {
Expects(this == &Auth());
_saveDataTimer.callOnce(delay);
}
@@ -447,7 +324,7 @@ void AuthSession::checkAutoLock() {
auto now = getms(true);
auto shouldLockInMs = Global::AutoLock() * 1000LL;
auto idleForMs = psIdleTime();
auto notPlayingVideoForMs = now - data().lastTimeVideoPlayedAt();
auto notPlayingVideoForMs = now - settings().lastTimeVideoPlayedAt();
auto checkTimeMs = qMin(idleForMs, notPlayingVideoForMs);
if (checkTimeMs >= shouldLockInMs || (_shouldLockAt > 0 && now > _shouldLockAt + kAutoLockTimeoutLateMs)) {
Messenger::Instance().setupPasscode();

View File

@@ -11,11 +11,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/filter.h>
#include <rpl/variable.h>
#include "base/timer.h"
#include "chat_helpers/stickers.h"
class ApiWrap;
enum class SendFilesWay;
namespace Data {
class Session;
} // namespace Data
namespace Storage {
class Downloader;
class Uploader;
@@ -41,65 +44,20 @@ namespace Core {
class Changelogs;
} // namespace Core
class AuthSessionData final {
class AuthSessionSettings final {
public:
base::Variable<bool> &contactsLoaded() {
return _contactsLoaded;
}
base::Variable<bool> &allChatsLoaded() {
return _allChatsLoaded;
}
base::Observable<void> &moreChatsLoaded() {
return _moreChatsLoaded;
}
base::Observable<void> &pendingHistoryResize() {
return _pendingHistoryResize;
}
struct ItemVisibilityQuery {
not_null<HistoryItem*> item;
not_null<bool*> isVisible;
};
base::Observable<ItemVisibilityQuery> &queryItemVisibility() {
return _queryItemVisibility;
}
void markItemLayoutChanged(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemLayoutChanged() const;
void requestItemRepaint(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemRepaintRequest() const;
void markItemRemoved(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemRemoved() const;
void markHistoryUnloaded(not_null<const History*> history);
rpl::producer<not_null<const History*>> historyUnloaded() const;
void markHistoryCleared(not_null<const History*> history);
rpl::producer<not_null<const History*>> historyCleared() const;
using MegagroupParticipant = std::tuple<
not_null<ChannelData*>,
not_null<UserData*>>;
void removeMegagroupParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user);
rpl::producer<MegagroupParticipant> megagroupParticipantRemoved() const;
rpl::producer<not_null<UserData*>> megagroupParticipantRemoved(
not_null<ChannelData*> channel) const;
void addNewMegagroupParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user);
rpl::producer<MegagroupParticipant> megagroupParticipantAdded() const;
rpl::producer<not_null<UserData*>> megagroupParticipantAdded(
not_null<ChannelData*> channel) const;
void moveFrom(AuthSessionData &&other) {
void moveFrom(AuthSessionSettings &&other) {
_variables = std::move(other._variables);
}
QByteArray serialize() const;
void constructFromSerialized(const QByteArray &serialized);
bool lastSeenWarningSeen() const {
return _variables.lastSeenWarningSeen;
}
void setLastSeenWarningSeen(bool lastSeenWarningSeen) {
_variables.lastSeenWarningSeen = lastSeenWarningSeen;
}
bool lastSeenWarningSeen() const {
return _variables.lastSeenWarningSeen;
}
void setSendFilesWay(SendFilesWay way) {
_variables.sendFilesWay = way;
}
@@ -176,10 +134,6 @@ public:
int thirdColumnWidth() const;
rpl::producer<int> thirdColumnWidthChanges() const;
void markStickersUpdated();
rpl::producer<> stickersUpdated() const;
void markSavedGifsUpdated();
rpl::producer<> savedGifsUpdated() const;
void setGroupStickersSectionHidden(PeerId peerId) {
_variables.groupStickersSectionHidden.insert(peerId);
}
@@ -189,79 +143,6 @@ public:
void removeGroupStickersSectionHidden(PeerId peerId) {
_variables.groupStickersSectionHidden.remove(peerId);
}
bool stickersUpdateNeeded(TimeMs now) const {
return stickersUpdateNeeded(_lastStickersUpdate, now);
}
void setLastStickersUpdate(TimeMs update) {
_lastStickersUpdate = update;
}
bool recentStickersUpdateNeeded(TimeMs now) const {
return stickersUpdateNeeded(_lastRecentStickersUpdate, now);
}
void setLastRecentStickersUpdate(TimeMs update) {
_lastRecentStickersUpdate = update;
}
bool favedStickersUpdateNeeded(TimeMs now) const {
return stickersUpdateNeeded(_lastFavedStickersUpdate, now);
}
void setLastFavedStickersUpdate(TimeMs update) {
_lastFavedStickersUpdate = update;
}
bool featuredStickersUpdateNeeded(TimeMs now) const {
return stickersUpdateNeeded(_lastFeaturedStickersUpdate, now);
}
void setLastFeaturedStickersUpdate(TimeMs update) {
_lastFeaturedStickersUpdate = update;
}
bool savedGifsUpdateNeeded(TimeMs now) const {
return stickersUpdateNeeded(_lastSavedGifsUpdate, now);
}
void setLastSavedGifsUpdate(TimeMs update) {
_lastSavedGifsUpdate = update;
}
int featuredStickerSetsUnreadCount() const {
return _featuredStickerSetsUnreadCount.current();
}
void setFeaturedStickerSetsUnreadCount(int count) {
_featuredStickerSetsUnreadCount = count;
}
rpl::producer<int> featuredStickerSetsUnreadCountValue() const {
return _featuredStickerSetsUnreadCount.value();
}
const Stickers::Sets &stickerSets() const {
return _stickerSets;
}
Stickers::Sets &stickerSetsRef() {
return _stickerSets;
}
const Stickers::Order &stickerSetsOrder() const {
return _stickerSetsOrder;
}
Stickers::Order &stickerSetsOrderRef() {
return _stickerSetsOrder;
}
const Stickers::Order &featuredStickerSetsOrder() const {
return _featuredStickerSetsOrder;
}
Stickers::Order &featuredStickerSetsOrderRef() {
return _featuredStickerSetsOrder;
}
const Stickers::Order &archivedStickerSetsOrder() const {
return _archivedStickerSetsOrder;
}
Stickers::Order &archivedStickerSetsOrderRef() {
return _archivedStickerSetsOrder;
}
const Stickers::SavedGifs &savedGifs() const {
return _savedGifs;
}
Stickers::SavedGifs &savedGifsRef() {
return _savedGifs;
}
HistoryItemsList idsToItems(const MessageIdsList &ids) const;
MessageIdsList itemsToIds(const HistoryItemsList &items) const;
MessageIdsList groupToIds(not_null<HistoryMessageGroup*> group) const;
private:
struct Variables {
@@ -288,39 +169,6 @@ private:
= kDefaultThirdColumnWidth; // per-window
};
bool stickersUpdateNeeded(TimeMs lastUpdate, TimeMs now) const {
constexpr auto kStickersUpdateTimeout = TimeMs(3600'000);
return (lastUpdate == 0)
|| (now >= lastUpdate + kStickersUpdateTimeout);
}
base::Variable<bool> _contactsLoaded = { false };
base::Variable<bool> _allChatsLoaded = { false };
base::Observable<void> _moreChatsLoaded;
base::Observable<void> _pendingHistoryResize;
base::Observable<ItemVisibilityQuery> _queryItemVisibility;
rpl::event_stream<not_null<const HistoryItem*>> _itemLayoutChanged;
rpl::event_stream<not_null<const HistoryItem*>> _itemRepaintRequest;
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
rpl::event_stream<not_null<const History*>> _historyUnloaded;
rpl::event_stream<not_null<const History*>> _historyCleared;
rpl::event_stream<MegagroupParticipant> _megagroupParticipantRemoved;
rpl::event_stream<MegagroupParticipant> _megagroupParticipantAdded;
rpl::event_stream<> _stickersUpdated;
rpl::event_stream<> _savedGifsUpdated;
TimeMs _lastStickersUpdate = 0;
TimeMs _lastRecentStickersUpdate = 0;
TimeMs _lastFavedStickersUpdate = 0;
TimeMs _lastFeaturedStickersUpdate = 0;
TimeMs _lastSavedGifsUpdate = 0;
rpl::variable<int> _featuredStickerSetsUnreadCount = 0;
Stickers::Sets _stickerSets;
Stickers::Order _stickerSetsOrder;
Stickers::Order _featuredStickerSetsOrder;
Stickers::Order _archivedStickerSetsOrder;
Stickers::SavedGifs _savedGifs;
rpl::event_stream<bool> _thirdSectionInfoEnabledValue;
bool _tabbedReplacedWithInfo = false;
rpl::event_stream<bool> _tabbedReplacedWithInfoValue;
@@ -334,7 +182,9 @@ private:
class AuthSession;
AuthSession &Auth();
class AuthSession final : private base::Subscriber {
class AuthSession final
: public base::has_weak_ptr
, private base::Subscriber {
public:
AuthSession(UserId userId);
@@ -368,10 +218,13 @@ public:
return *_notifications;
}
AuthSessionData &data() {
return _data;
Data::Session &data() {
return *_data;
}
void saveDataDelayed(TimeMs delay = kDefaultSaveDelay);
AuthSessionSettings &settings() {
return _settings;
}
void saveSettingsDelayed(TimeMs delay = kDefaultSaveDelay);
ApiWrap &api() {
return *_api;
@@ -393,7 +246,7 @@ private:
static constexpr auto kDefaultSaveDelay = TimeMs(1000);
const UserId _userId = 0;
AuthSessionData _data;
AuthSessionSettings _settings;
base::Timer _saveDataTimer;
TimeMs _shouldLockAt = 0;
@@ -405,6 +258,13 @@ private:
const std::unique_ptr<Storage::Uploader> _uploader;
const std::unique_ptr<Storage::Facade> _storage;
const std::unique_ptr<Window::Notifications::System> _notifications;
// _data depends on _downloader / _uploader, including destructor.
const std::unique_ptr<Data::Session> _data;
// _changelogs depends on _data, subscribes on chats loading event.
const std::unique_ptr<Core::Changelogs> _changelogs;
rpl::lifetime _lifetime;
};

View File

@@ -31,7 +31,7 @@ const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask) {
return i.value();
}
const RuntimeComposerMetadata *RuntimeComposer::ZeroRuntimeComposerMetadata = GetRuntimeComposerMetadata(0);
const RuntimeComposerMetadata *RuntimeComposerBase::ZeroRuntimeComposerMetadata = GetRuntimeComposerMetadata(0);
RuntimeComponentWrapStruct RuntimeComponentWraps[64];

View File

@@ -7,8 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
template <typename Base>
class RuntimeComposer;
typedef void(*RuntimeComponentConstruct)(void *location, RuntimeComposer *composer);
class RuntimeComposerBase;
typedef void(*RuntimeComponentConstruct)(void *location, RuntimeComposerBase *composer);
typedef void(*RuntimeComponentDestruct)(void *location);
typedef void(*RuntimeComponentMove)(void *location, void *waslocation);
@@ -38,8 +41,10 @@ struct CeilDivideMinimumOne {
extern RuntimeComponentWrapStruct RuntimeComponentWraps[64];
extern QAtomicInt RuntimeComponentIndexLast;
template <typename Type>
template <typename Type, typename Base>
struct RuntimeComponent {
using RuntimeComponentBase = Base;
RuntimeComponent() {
// While there is no std::aligned_alloc().
static_assert(alignof(Type) <= alignof(std::max_align_t), "Components should align to std::max_align_t!");
@@ -76,7 +81,7 @@ struct RuntimeComponent {
}
protected:
static void RuntimeComponentConstruct(void *location, RuntimeComposer *composer) {
static void RuntimeComponentConstruct(void *location, RuntimeComposerBase *composer) {
new (location) Type();
}
static void RuntimeComponentDestruct(void *location) {
@@ -134,9 +139,9 @@ private:
const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask);
class RuntimeComposer {
class RuntimeComposerBase {
public:
RuntimeComposer(uint64 mask = 0) : _data(zerodata()) {
RuntimeComposerBase(uint64 mask = 0) : _data(zerodata()) {
if (mask) {
auto meta = GetRuntimeComposerMetadata(mask);
@@ -169,9 +174,9 @@ public:
}
}
}
RuntimeComposer(const RuntimeComposer &other) = delete;
RuntimeComposer &operator=(const RuntimeComposer &other) = delete;
~RuntimeComposer() {
RuntimeComposerBase(const RuntimeComposerBase &other) = delete;
RuntimeComposerBase &operator=(const RuntimeComposerBase &other) = delete;
~RuntimeComposerBase() {
if (_data != zerodata()) {
auto meta = _meta();
for (int i = 0; i < meta->last; ++i) {
@@ -184,45 +189,40 @@ public:
}
}
template <typename Type>
bool Has() const {
return (_meta()->offsets[Type::Index()] >= sizeof(_meta()));
}
template <typename Type>
Type *Get() {
return static_cast<Type*>(_dataptr(_meta()->offsets[Type::Index()]));
}
template <typename Type>
const Type *Get() const {
return static_cast<const Type*>(_dataptr(_meta()->offsets[Type::Index()]));
}
protected:
void UpdateComponents(uint64 mask = 0) {
if (!_meta()->equals(mask)) {
RuntimeComposer tmp(mask);
tmp.swap(*this);
if (_data != zerodata() && tmp._data != zerodata()) {
auto meta = _meta(), wasmeta = tmp._meta();
for (int i = 0; i < meta->last; ++i) {
auto offset = meta->offsets[i];
auto wasoffset = wasmeta->offsets[i];
if (offset >= sizeof(_meta()) && wasoffset >= sizeof(_meta())) {
RuntimeComponentWraps[i].Move(_dataptrunsafe(offset), tmp._dataptrunsafe(wasoffset));
}
bool UpdateComponents(uint64 mask = 0) {
if (_meta()->equals(mask)) {
return false;
}
RuntimeComposerBase result(mask);
result.swap(*this);
if (_data != zerodata() && result._data != zerodata()) {
const auto meta = _meta();
const auto wasmeta = result._meta();
for (auto i = 0; i != meta->last; ++i) {
const auto offset = meta->offsets[i];
const auto wasoffset = wasmeta->offsets[i];
if (offset >= sizeof(_meta())
&& wasoffset >= sizeof(_meta())) {
RuntimeComponentWraps[i].Move(
_dataptrunsafe(offset),
result._dataptrunsafe(wasoffset));
}
}
}
return true;
}
void AddComponents(uint64 mask = 0) {
UpdateComponents(_meta()->maskadd(mask));
bool AddComponents(uint64 mask = 0) {
return UpdateComponents(_meta()->maskadd(mask));
}
void RemoveComponents(uint64 mask = 0) {
UpdateComponents(_meta()->maskremove(mask));
bool RemoveComponents(uint64 mask = 0) {
return UpdateComponents(_meta()->maskremove(mask));
}
private:
template <typename Base>
friend class RuntimeComposer;
static const RuntimeComposerMetadata *ZeroRuntimeComposerMetadata;
static void *zerodata() {
return &ZeroRuntimeComposerMetadata;
@@ -239,8 +239,41 @@ private:
}
void *_data = nullptr;
void swap(RuntimeComposer &other) {
void swap(RuntimeComposerBase &other) {
std::swap(_data, other._data);
}
};
template <typename Base>
class RuntimeComposer : public RuntimeComposerBase {
public:
using RuntimeComposerBase::RuntimeComposerBase;
template <
typename Type,
typename = std::enable_if_t<std::is_same_v<
typename Type::RuntimeComponentBase,
Base>>>
bool Has() const {
return (_meta()->offsets[Type::Index()] >= sizeof(_meta()));
}
template <
typename Type,
typename = std::enable_if_t<std::is_same_v<
typename Type::RuntimeComponentBase,
Base>>>
Type *Get() {
return static_cast<Type*>(_dataptr(_meta()->offsets[Type::Index()]));
}
template <
typename Type,
typename = std::enable_if_t<std::is_same_v<
typename Type::RuntimeComponentBase,
Base>>>
const Type *Get() const {
return static_cast<const Type*>(_dataptr(_meta()->offsets[Type::Index()]));
}
};

View File

@@ -35,6 +35,7 @@ namespace {
constexpr auto kMaxGroupChannelTitle = 255;
constexpr auto kMaxChannelDescription = 255;
constexpr auto kMaxBioLength = 70;
constexpr auto kMinUsernameLength = 5;
style::InputField CreateBioFieldStyle() {
auto result = st::newGroupDescription;
@@ -241,19 +242,23 @@ bool AddContactBox::onSaveUserFail(const RPCError &error) {
void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
if (!isBoxShown() || !App::main()) return;
auto &d = res.c_contacts_importedContacts();
const auto &d = res.c_contacts_importedContacts();
App::feedUsers(d.vusers);
auto &v = d.vimported.v;
UserData *user = nullptr;
if (!v.isEmpty()) {
auto &c = v.front().c_importedContact();
if (c.vclient_id.v != _contactId) return;
user = App::userLoaded(c.vuser_id.v);
}
const auto &v = d.vimported.v;
const auto user = [&]() -> UserData* {
if (!v.isEmpty()) {
auto &c = v.front().c_importedContact();
if (c.vclient_id.v == _contactId) {
return App::userLoaded(c.vuser_id.v);
}
}
return nullptr;
}();
if (user) {
Notify::userIsContactChanged(user, true);
if (user->contactStatus() == UserData::ContactStatus::Contact) {
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
}
Ui::hideLayer();
} else {
hideChildren();
@@ -763,7 +768,7 @@ void SetupChannelBox::onChange() {
return;
}
}
if (name.size() < MinUsernameLength) {
if (name.size() < kMinUsernameLength) {
if (_errorText != lang(lng_create_channel_link_too_short)) {
_errorText = lang(lng_create_channel_link_too_short);
update();
@@ -784,9 +789,14 @@ void SetupChannelBox::onCheck() {
MTP::cancel(_checkRequestId);
}
QString link = _link->text().trimmed();
if (link.size() >= MinUsernameLength) {
if (link.size() >= kMinUsernameLength) {
_checkUsername = link;
_checkRequestId = MTP::send(MTPchannels_CheckUsername(_channel->inputChannel, MTP_string(link)), rpcDone(&SetupChannelBox::onCheckDone), rpcFail(&SetupChannelBox::onCheckFail));
_checkRequestId = MTP::send(
MTPchannels_CheckUsername(
_channel->inputChannel,
MTP_string(link)),
rpcDone(&SetupChannelBox::onCheckDone),
rpcFail(&SetupChannelBox::onCheckFail));
}
}

View File

@@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "apiwrap.h"
#include "application.h"
#include "history/history.h"
#include "history/history_item.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
@@ -20,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/empty_userpic.h"
#include "core/click_handler_types.h"
#include "storage/localstorage.h"
#include "data/data_session.h"
#include "auth_session.h"
#include "observer_peer.h"
@@ -457,7 +460,7 @@ void DeleteMessagesBox::prepare() {
} else {
text = _singleItem ? lang(lng_selected_delete_sure_this) : lng_selected_delete_sure(lt_count, _ids.size());
auto canDeleteAllForEveryone = true;
auto now = ::date(unixtime());
auto now = unixtime();
auto deleteForUser = (UserData*)nullptr;
auto peer = (PeerData*)nullptr;
auto forEveryoneText = lang(lng_delete_for_everyone_check);
@@ -565,28 +568,28 @@ void DeleteMessagesBox::deleteAndClear() {
MTP_vector<MTPint>(1, MTP_int(_ids[0].msg))));
}
if (_deleteAll && _deleteAll->checked()) {
App::main()->deleteAllFromUser(
Auth().api().deleteAllFromUser(
_moderateInChannel,
_moderateFrom);
}
}
if (!_singleItem) {
App::main()->clearSelectedItems();
if (_deleteConfirmedCallback) {
_deleteConfirmedCallback();
}
QMap<PeerData*, QVector<MTPint>> idsByPeer;
for_const (auto fullId, _ids) {
if (auto item = App::histItemById(fullId)) {
for (const auto itemId : _ids) {
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(fullId.msg));
} else if (wasLast) {
App::main()->checkPeerHistory(history->peer);
idsByPeer[history->peer].push_back(MTP_int(itemId.msg));
} else if (wasLast && !history->lastMessageKnown()) {
Auth().api().requestDialogEntry(history);
}
}
}
@@ -596,6 +599,7 @@ void DeleteMessagesBox::deleteAndClear() {
App::main()->deleteMessages(i.key(), i.value(), forEveryone);
}
Ui::hideLayer();
Auth().data().sendHistoryChangeNotifications();
}
ConfirmInviteBox::ConfirmInviteBox(QWidget*, const QString &title, bool isChannel, const MTPChatPhoto &photo, int count, const QVector<UserData*> &participants)

View File

@@ -166,6 +166,10 @@ public:
bool suggestModerateActions);
DeleteMessagesBox(QWidget*, MessageIdsList &&selected);
void setDeleteConfirmedCallback(base::lambda<void()> callback) {
_deleteConfirmedCallback = std::move(callback);
}
protected:
void prepare() override;
@@ -188,6 +192,8 @@ private:
object_ptr<Ui::Checkbox> _reportSpam = { nullptr };
object_ptr<Ui::Checkbox> _deleteAll = { nullptr };
base::lambda<void()> _deleteConfirmedCallback;
};
class ConfirmInviteBox : public BoxContent, public RPCSender {

View File

@@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "auth_session.h"
#include "data/data_session.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
@@ -291,55 +293,63 @@ void AutoDownloadBox::resizeEvent(QResizeEvent *e) {
}
void AutoDownloadBox::onSave() {
bool changed = false;
int32 autoDownloadPhoto = (_photoPrivate->checked() ? 0 : dbiadNoPrivate) | (_photoGroups->checked() ? 0 : dbiadNoGroups);
auto photosChanged = false;
auto documentsChanged = false;
auto autoplayChanged = false;
auto photosEnabled = false;
auto voiceEnabled = false;
auto animationsEnabled = false;
auto autoDownloadPhoto = (_photoPrivate->checked() ? 0 : dbiadNoPrivate)
| (_photoGroups->checked() ? 0 : dbiadNoGroups);
if (cAutoDownloadPhoto() != autoDownloadPhoto) {
bool enabledPrivate = ((cAutoDownloadPhoto() & dbiadNoPrivate) && !(autoDownloadPhoto & dbiadNoPrivate));
bool enabledGroups = ((cAutoDownloadPhoto() & dbiadNoGroups) && !(autoDownloadPhoto & dbiadNoGroups));
const auto enabledPrivate = (cAutoDownloadPhoto() & dbiadNoPrivate)
&& !(autoDownloadPhoto & dbiadNoPrivate);
const auto enabledGroups = (cAutoDownloadPhoto() & dbiadNoGroups)
&& !(autoDownloadPhoto & dbiadNoGroups);
photosEnabled = enabledPrivate || enabledGroups;
photosChanged = true;
cSetAutoDownloadPhoto(autoDownloadPhoto);
if (enabledPrivate || enabledGroups) {
const PhotosData &data(App::photosData());
for (PhotosData::const_iterator i = data.cbegin(), e = data.cend(); i != e; ++i) {
i.value()->automaticLoadSettingsChanged();
}
}
changed = true;
}
int32 autoDownloadAudio = (_audioPrivate->checked() ? 0 : dbiadNoPrivate) | (_audioGroups->checked() ? 0 : dbiadNoGroups);
auto autoDownloadAudio = (_audioPrivate->checked() ? 0 : dbiadNoPrivate)
| (_audioGroups->checked() ? 0 : dbiadNoGroups);
if (cAutoDownloadAudio() != autoDownloadAudio) {
bool enabledPrivate = ((cAutoDownloadAudio() & dbiadNoPrivate) && !(autoDownloadAudio & dbiadNoPrivate));
bool enabledGroups = ((cAutoDownloadAudio() & dbiadNoGroups) && !(autoDownloadAudio & dbiadNoGroups));
const auto enabledPrivate = (cAutoDownloadAudio() & dbiadNoPrivate)
&& !(autoDownloadAudio & dbiadNoPrivate);
const auto enabledGroups = (cAutoDownloadAudio() & dbiadNoGroups)
&& !(autoDownloadAudio & dbiadNoGroups);
voiceEnabled = enabledPrivate || enabledGroups;
documentsChanged = true;
cSetAutoDownloadAudio(autoDownloadAudio);
if (enabledPrivate || enabledGroups) {
for (auto document : App::documentsData()) {
if (document->isVoiceMessage()) {
document->automaticLoadSettingsChanged();
}
}
}
changed = true;
}
int32 autoDownloadGif = (_gifPrivate->checked() ? 0 : dbiadNoPrivate) | (_gifGroups->checked() ? 0 : dbiadNoGroups);
auto autoDownloadGif = (_gifPrivate->checked() ? 0 : dbiadNoPrivate)
| (_gifGroups->checked() ? 0 : dbiadNoGroups);
if (cAutoDownloadGif() != autoDownloadGif) {
bool enabledPrivate = ((cAutoDownloadGif() & dbiadNoPrivate) && !(autoDownloadGif & dbiadNoPrivate));
bool enabledGroups = ((cAutoDownloadGif() & dbiadNoGroups) && !(autoDownloadGif & dbiadNoGroups));
const auto enabledPrivate = (cAutoDownloadGif() & dbiadNoPrivate)
&& !(autoDownloadGif & dbiadNoPrivate);
const auto enabledGroups = (cAutoDownloadGif() & dbiadNoGroups)
&& !(autoDownloadGif & dbiadNoGroups);
animationsEnabled = enabledPrivate || enabledGroups;
documentsChanged = true;
cSetAutoDownloadGif(autoDownloadGif);
if (enabledPrivate || enabledGroups) {
for (auto document : App::documentsData()) {
if (document->isAnimation()) {
document->automaticLoadSettingsChanged();
}
}
}
changed = true;
}
if (cAutoPlayGif() != _gifPlay->checked()) {
cSetAutoPlayGif(_gifPlay->checked());
if (!cAutoPlayGif()) {
App::stopGifItems();
Auth().data().stopAutoplayAnimations();
}
changed = true;
autoplayChanged = true;
}
if (photosChanged || documentsChanged || autoplayChanged) {
Local::writeUserSettings();
}
if (photosEnabled) {
Auth().data().photoLoadSettingsChanged();
}
if (voiceEnabled) {
Auth().data().voiceLoadSettingsChanged();
}
if (animationsEnabled) {
Auth().data().animationLoadSettingsChanged();
}
if (changed) Local::writeUserSettings();
closeBox();
}

View File

@@ -10,68 +10,47 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/input_fields.h"
#include "ui/text_options.h"
#include "media/media_clip_reader.h"
#include "history/history_media_types.h"
#include "history/history.h"
#include "history/history_item.h"
#include "data/data_media_types.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "lang/lang_keys.h"
#include "window/window_controller.h"
#include "mainwidget.h"
#include "layout.h"
#include "styles/style_history.h"
#include "styles/style_boxes.h"
EditCaptionBox::EditCaptionBox(
QWidget*,
not_null<HistoryMedia*> media,
FullMsgId msgId)
: _msgId(msgId) {
Expects(media->canEditCaption());
not_null<HistoryItem*> item)
: _msgId(item->fullId()) {
Expects(item->media() != nullptr);
Expects(item->media()->allowsEditCaption());
QSize dimensions;
ImagePtr image;
QString caption;
DocumentData *doc = nullptr;
switch (media->type()) {
case MediaTypeGif: {
_animated = true;
doc = static_cast<HistoryGif*>(media.get())->getDocument();
dimensions = doc->dimensions;
image = doc->thumb;
} break;
case MediaTypePhoto: {
const auto media = item->media();
if (const auto photo = media->photo()) {
_photo = true;
auto photo = static_cast<HistoryPhoto*>(media.get())->getPhoto();
dimensions = QSize(photo->full->width(), photo->full->height());
image = photo->full;
} break;
case MediaTypeVideo: {
_animated = true;
doc = static_cast<HistoryVideo*>(media.get())->getDocument();
dimensions = doc->dimensions;
image = doc->thumb;
} break;
case MediaTypeGrouped: {
if (const auto photo = media->getPhoto()) {
dimensions = QSize(photo->full->width(), photo->full->height());
image = photo->full;
_photo = true;
} else if (const auto doc = media->getDocument()) {
dimensions = doc->dimensions;
image = doc->thumb;
} else if (const auto document = media->document()) {
dimensions = document->dimensions;
image = document->thumb;
if (document->isAnimation()) {
_animated = true;
} else if (document->isVideoFile()) {
_animated = true;
} else {
_doc = true;
}
} break;
case MediaTypeFile:
case MediaTypeMusicFile:
case MediaTypeVoiceFile: {
_doc = true;
doc = static_cast<HistoryDocument*>(media.get())->getDocument();
image = doc->thumb;
} break;
doc = document;
}
caption = media->getCaption().text;
auto caption = item->originalText().text;
if (!_animated && (dimensions.isEmpty() || doc || image->isNull())) {
if (image->isNull()) {

View File

@@ -9,13 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
namespace Data {
class Media;
} // namespace Data
namespace Ui {
class InputArea;
} // namespace Ui
class EditCaptionBox : public BoxContent, public RPCSender {
public:
EditCaptionBox(QWidget*, not_null<HistoryMedia*> media, FullMsgId msgId);
EditCaptionBox(QWidget*, not_null<HistoryItem*> item);
protected:
void prepare() override;

View File

@@ -415,7 +415,7 @@ MTPChannelBannedRights EditRestrictedBox::DefaultRights(not_null<ChannelData*> c
void EditRestrictedBox::showRestrictUntil() {
auto tomorrow = QDate::currentDate().addDays(1);
auto highlighted = isUntilForever() ? tomorrow : date(getRealUntilValue()).date();
auto highlighted = isUntilForever() ? tomorrow : ParseDateTime(getRealUntilValue()).date();
auto month = highlighted;
_restrictUntilBox = Ui::show(
Box<CalendarBox>(
@@ -471,7 +471,11 @@ void EditRestrictedBox::createUntilVariants() {
};
auto addCustomVariant = [this, addVariant](TimeId until, TimeId from, TimeId to) {
if (!ChannelData::IsRestrictedForever(until) && until > from && until <= to) {
addVariant(until, lng_rights_chat_banned_custom_date(lt_date, langDayOfMonthFull(date(until).date())));
addVariant(
until,
lng_rights_chat_banned_custom_date(
lt_date,
langDayOfMonthFull(ParseDateTime(until).date())));
}
};
auto addCurrentVariant = [this, addCustomVariant](TimeId from, TimeId to) {

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/wrap/slide_wrap.h"
#include "history/history.h"
#include "boxes/peer_list_controllers.h"
#include "apiwrap.h"
#include "auth_session.h"

View File

@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "mainwindow.h"
#include "auth_session.h"
#include "layout.h"
LocalStorageBox::LocalStorageBox(QWidget *parent)
: _clear(this, lang(lng_local_storage_clear), st::boxLinkButton) {

View File

@@ -250,6 +250,11 @@ void PeerListController::setSearchNoResultsText(const QString &text) {
}
}
base::unique_qptr<Ui::PopupMenu> PeerListController::rowContextMenu(
not_null<PeerListRow*> row) {
return nullptr;
}
std::unique_ptr<PeerListState> PeerListController::saveState() const {
return delegate()->peerListSaveState();
}
@@ -563,6 +568,7 @@ PeerListContent::PeerListContent(
void PeerListContent::appendRow(std::unique_ptr<PeerListRow> row) {
Expects(row != nullptr);
if (_rowsById.find(row->id()) == _rowsById.cend()) {
row->setAbsoluteIndex(_rows.size());
addRowEntry(row.get());
@@ -573,6 +579,7 @@ void PeerListContent::appendRow(std::unique_ptr<PeerListRow> row) {
void PeerListContent::appendSearchRow(std::unique_ptr<PeerListRow> row) {
Expects(row != nullptr);
Expects(showingSearch());
if (_rowsById.find(row->id()) == _rowsById.cend()) {
row->setAbsoluteIndex(_searchRows.size());
row->setIsSearchResult(true);
@@ -584,13 +591,17 @@ void PeerListContent::appendSearchRow(std::unique_ptr<PeerListRow> row) {
void PeerListContent::appendFoundRow(not_null<PeerListRow*> row) {
Expects(showingSearch());
auto index = findRowIndex(row);
if (index.value < 0) {
_filterResults.push_back(row);
}
}
void PeerListContent::changeCheckState(not_null<PeerListRow*> row, bool checked, PeerListRow::SetStyle style) {
void PeerListContent::changeCheckState(
not_null<PeerListRow*> row,
bool checked,
PeerListRow::SetStyle style) {
row->setChecked(
checked,
style,
@@ -629,16 +640,16 @@ void PeerListContent::addToSearchIndex(not_null<PeerListRow*> row) {
}
removeFromSearchIndex(row);
row->setNameFirstChars(row->peer()->nameFirstChars());
for (auto ch : row->nameFirstChars()) {
row->setNameFirstLetters(row->peer()->nameFirstLetters());
for (auto ch : row->nameFirstLetters()) {
_searchIndex[ch].push_back(row);
}
}
void PeerListContent::removeFromSearchIndex(not_null<PeerListRow*> row) {
auto &nameFirstChars = row->nameFirstChars();
if (!nameFirstChars.empty()) {
for (auto ch : row->nameFirstChars()) {
const auto &nameFirstLetters = row->nameFirstLetters();
if (!nameFirstLetters.empty()) {
for (auto ch : row->nameFirstLetters()) {
auto it = _searchIndex.find(ch);
if (it != _searchIndex.cend()) {
auto &entry = it->second;
@@ -648,7 +659,7 @@ void PeerListContent::removeFromSearchIndex(not_null<PeerListRow*> row) {
}
}
}
row->setNameFirstChars({});
row->setNameFirstLetters({});
}
}
@@ -1008,7 +1019,7 @@ void PeerListContent::contextMenuEvent(QContextMenuEvent *e) {
mousePressReleased(_pressButton);
}
if (auto row = getRow(_contexted.index)) {
if (const auto row = getRow(_contexted.index)) {
_contextMenu = _controller->rowContextMenu(row);
if (_contextMenu) {
_contextMenu->setDestroyedCallback(base::lambda_guarded(

View File

@@ -177,11 +177,11 @@ public:
int outerWidth);
float64 checkedRatio();
void setNameFirstChars(const base::flat_set<QChar> &nameFirstChars) {
_nameFirstChars = nameFirstChars;
void setNameFirstLetters(const base::flat_set<QChar> &firstLetters) {
_nameFirstLetters = firstLetters;
}
const base::flat_set<QChar> &nameFirstChars() const {
return _nameFirstChars;
const base::flat_set<QChar> &nameFirstLetters() const {
return _nameFirstLetters;
}
virtual void lazyInitialize(const style::PeerListItem &st);
@@ -218,7 +218,7 @@ private:
Text _status;
StatusType _statusType = StatusType::Online;
TimeMs _statusValidTill = 0;
base::flat_set<QChar> _nameFirstChars;
base::flat_set<QChar> _nameFirstLetters;
int _absoluteIndex = -1;
State _disabledState = State::Active;
bool _initialized : 1;
@@ -344,10 +344,8 @@ public:
}
virtual void itemDeselectedHook(not_null<PeerData*> peer) {
}
virtual Ui::PopupMenu *rowContextMenu(
not_null<PeerListRow*> row) {
return nullptr;
}
virtual base::unique_qptr<Ui::PopupMenu> rowContextMenu(
not_null<PeerListRow*> row);
bool isSearchLoading() const {
return _searchController ? _searchController->isLoading() : false;
}
@@ -360,7 +358,7 @@ public:
return nullptr;
}
virtual std::unique_ptr<PeerListState> saveState() const ;
virtual std::unique_ptr<PeerListState> saveState() const;
virtual void restoreState(
std::unique_ptr<PeerListState> state);
@@ -625,7 +623,7 @@ private:
std::vector<std::unique_ptr<PeerListRow>> _searchRows;
base::Timer _repaintByStatus;
QPointer<Ui::PopupMenu> _contextMenu;
base::unique_qptr<Ui::PopupMenu> _contextMenu;
};

View File

@@ -13,9 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "observer_peer.h"
#include "ui/widgets/checkbox.h"
#include "auth_session.h"
#include "data/data_session.h"
#include "apiwrap.h"
#include "mainwidget.h"
#include "lang/lang_keys.h"
#include "history/history.h"
#include "dialogs/dialogs_indexed_list.h"
namespace {
@@ -147,7 +149,7 @@ void PeerListGlobalSearchController::searchQuery(const QString &query) {
if (_query != query) {
_query = query;
_requestId = 0;
if (_query.size() >= MinUsernameLength && !searchInCache()) {
if (!_query.isEmpty() && !searchInCache()) {
_timer.callOnce(AutoSearchTimeout);
} else {
_timer.cancel();
@@ -166,9 +168,12 @@ bool PeerListGlobalSearchController::searchInCache() {
}
void PeerListGlobalSearchController::searchOnServer() {
_requestId = request(MTPcontacts_Search(MTP_string(_query), MTP_int(SearchPeopleLimit))).done([this](const MTPcontacts_Found &result, mtpRequestId requestId) {
_requestId = request(MTPcontacts_Search(
MTP_string(_query),
MTP_int(SearchPeopleLimit)
)).done([=](const MTPcontacts_Found &result, mtpRequestId requestId) {
searchDone(result, requestId);
}).fail([this](const RPCError &error, mtpRequestId requestId) {
}).fail([=](const RPCError &error, mtpRequestId requestId) {
if (_requestId == requestId) {
_requestId = 0;
delegate()->peerListSearchRefreshRows();
@@ -177,7 +182,9 @@ void PeerListGlobalSearchController::searchOnServer() {
_queries.emplace(_requestId, _query);
}
void PeerListGlobalSearchController::searchDone(const MTPcontacts_Found &result, mtpRequestId requestId) {
void PeerListGlobalSearchController::searchDone(
const MTPcontacts_Found &result,
mtpRequestId requestId) {
Expects(result.type() == mtpc_contacts_found);
auto &contacts = result.c_contacts_found();
@@ -192,13 +199,17 @@ void PeerListGlobalSearchController::searchDone(const MTPcontacts_Found &result,
_queries.erase(it);
}
}
if (_requestId == requestId) {
_requestId = 0;
for_const (auto &mtpPeer, contacts.vresults.v) {
if (auto peer = App::peerLoaded(peerFromMTP(mtpPeer))) {
const auto feedList = [&](const MTPVector<MTPPeer> &list) {
for (const auto &mtpPeer : list.v) {
if (const auto peer = App::peerLoaded(peerFromMTP(mtpPeer))) {
delegate()->peerListSearchAddRow(peer);
}
}
};
if (_requestId == requestId) {
_requestId = 0;
feedList(contacts.vmy_results);
feedList(contacts.vresults);
delegate()->peerListSearchRefreshRows();
}
}
@@ -207,6 +218,11 @@ bool PeerListGlobalSearchController::isLoading() {
return _timer.isActive() || _requestId;
}
ChatsListBoxController::Row::Row(not_null<History*> history)
: PeerListRow(history->peer)
, _history(history) {
}
ChatsListBoxController::ChatsListBoxController(
std::unique_ptr<PeerListSearchController> searchController)
: PeerListController(std::move(searchController)) {
@@ -236,9 +252,11 @@ void ChatsListBoxController::rebuildRows() {
auto wasEmpty = !delegate()->peerListFullRowsCount();
auto appendList = [this](auto chats) {
auto count = 0;
for_const (auto row, chats->all()) {
if (appendRow(row->history())) {
++count;
for (const auto row : chats->all()) {
if (const auto history = row->history()) {
if (appendRow(history)) {
++count;
}
}
}
return count;
@@ -320,11 +338,12 @@ void ContactsBoxController::prepare() {
void ContactsBoxController::rebuildRows() {
auto appendList = [this](auto chats) {
auto count = 0;
for_const (auto row, chats->all()) {
auto history = row->history();
if (auto user = history->peer->asUser()) {
if (appendRow(user)) {
++count;
for (const auto row : chats->all()) {
if (const auto history = row->history()) {
if (const auto user = history->peer->asUser()) {
if (appendRow(user)) {
++count;
}
}
}
}
@@ -345,8 +364,9 @@ void ContactsBoxController::checkForEmptyRows() {
}
}
std::unique_ptr<PeerListRow> ContactsBoxController::createSearchRow(not_null<PeerData*> peer) {
if (auto user = peer->asUser()) {
std::unique_ptr<PeerListRow> ContactsBoxController::createSearchRow(
not_null<PeerData*> peer) {
if (const auto user = peer->asUser()) {
return createRow(user);
}
return nullptr;
@@ -748,8 +768,10 @@ void AddBotToGroupBoxController::shareBotGame(not_null<PeerData*> chat) {
MTP_inputGameShortName(
bot->inputUser,
MTP_string(bot->botInfo->shareGameShortName))),
MTP_string(""),
MTP_long(randomId),
MTPnullMarkup),
MTPnullMarkup,
MTPnullEntities),
App::main()->rpcDone(&MainWidget::sentUpdatesReceived),
App::main()->rpcFail(&MainWidget::sendMessageFail),
0,

View File

@@ -91,8 +91,8 @@ public:
protected:
class Row : public PeerListRow {
public:
Row(not_null<History*> history) : PeerListRow(history->peer), _history(history) {
}
Row(not_null<History*> history);
not_null<History*> history() const {
return _history;
}

View File

@@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
constexpr auto kUsernameCheckTimeout = TimeMs(200);
constexpr auto kMinUsernameLength = 5;
class Controller
: private MTP::Sender
@@ -503,7 +504,7 @@ void Controller::checkUsernameAvailability() {
auto checking = initial
? qsl(".bad.")
: _controls.username->getLastText().trimmed();
if (checking.size() < MinUsernameLength) {
if (checking.size() < kMinUsernameLength) {
return;
}
if (_checkUsernameRequestId) {
@@ -580,7 +581,7 @@ void Controller::usernameChanged() {
if (bad) {
showUsernameError(
Lang::Viewer(lng_create_channel_link_bad_symbols));
} else if (username.size() < MinUsernameLength) {
} else if (username.size() < kMinUsernameLength) {
showUsernameError(
Lang::Viewer(lng_create_channel_link_too_short));
} else {

View File

@@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/edit_peer_info_box.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/labels.h"
#include "history/history_admin_log_section.h"
#include "history/admin_log/history_admin_log_section.h"
#include "window/window_controller.h"
#include "profile/profile_channel_controllers.h"
#include "info/profile/info_profile_button.h"

View File

@@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_controller.h"
#include "styles/style_history.h"
#include "styles/style_boxes.h"
#include "layout.h"
namespace {
@@ -783,7 +784,11 @@ void SingleFilePreview::preparePreview(const Storage::PreparedFile &file) {
prepareThumb(preview);
const auto filepath = file.path;
if (filepath.isEmpty()) {
auto filename = filedialogDefaultName(qsl("image"), qsl(".png"), QString(), true);
auto filename = filedialogDefaultName(
qsl("image"),
qsl(".png"),
QString(),
true);
_nameText.setText(
st::semiboldTextStyle,
filename,
@@ -1433,7 +1438,7 @@ void SendFilesBox::initSendWay() {
? SendFilesWay::Album
: SendFilesWay::Photos;
}
const auto currentWay = Auth().data().sendFilesWay();
const auto currentWay = Auth().settings().sendFilesWay();
if (currentWay == SendFilesWay::Files) {
return currentWay;
} else if (currentWay == SendFilesWay::Album) {
@@ -1756,7 +1761,7 @@ void SendFilesBox::send(bool ctrlShiftEnter) {
const auto way = _sendWay ? _sendWay->value() : Way::Files;
if (_compressConfirm == CompressConfirm::Auto) {
const auto oldWay = Auth().data().sendFilesWay();
const auto oldWay = Auth().settings().sendFilesWay();
if (way != oldWay) {
// Check if the user _could_ use the old value, but didn't.
if ((oldWay == Way::Album && _sendAlbum)
@@ -1764,8 +1769,8 @@ void SendFilesBox::send(bool ctrlShiftEnter) {
|| (oldWay == Way::Files && _sendFiles)
|| (way == Way::Files && (_sendAlbum || _sendPhotos))) {
// And in that case save it to settings.
Auth().data().setSendFilesWay(way);
Auth().saveDataDelayed();
Auth().settings().setSendFilesWay(way);
Auth().saveSettingsDelayed();
}
}
}

View File

@@ -119,8 +119,9 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
//CountriesByISO2::const_iterator j = countries.constFind(country);
//if (j != countries.cend()) country = QString::fromUtf8(j.value()->name);
MTPint active = d.vdate_active.v ? d.vdate_active : d.vdate_created;
data.activeTime = active.v;
const auto active = data.activeTime = d.vdate_active.v
? d.vdate_active.v
: d.vdate_created.v;
data.info = qs(d.vdevice_model) + qstr(", ") + (platform.isEmpty() ? QString() : platform + ' ') + qs(d.vsystem_version);
data.ip = qs(d.vip) + (country.isEmpty() ? QString() : QString::fromUtf8(" \xe2\x80\x93 ") + country);
@@ -144,12 +145,15 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
}
_current = data;
} else {
QDateTime now(QDateTime::currentDateTime()), lastTime(date(active));
QDate nowDate(now.date()), lastDate(lastTime.date());
const auto now = QDateTime::currentDateTime();
const auto lastTime = ParseDateTime(active);
const auto nowDate = now.date();
const auto lastDate = lastTime.date();
QString dt;
if (lastDate == nowDate) {
data.active = lastTime.toString(cTimeFormat());
} else if (lastDate.year() == nowDate.year() && lastDate.weekNumber() == nowDate.weekNumber()) {
} else if (lastDate.year() == nowDate.year()
&& lastDate.weekNumber() == nowDate.weekNumber()) {
data.active = langDayOfWeek(lastDate);
} else {
data.active = lastDate.toString(qsl("d.MM.yy"));

View File

@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/text_options.h"
#include "history/history.h"
#include "history/history_media_types.h"
#include "history/history_message.h"
#include "window/themes/window_theme.h"
@@ -92,7 +93,7 @@ bool ShareBox::onSearchByUsername(bool searchCache) {
}
return true;
}
if (query.size() >= MinUsernameLength) {
if (!query.isEmpty()) {
if (searchCache) {
auto i = _peopleCache.constFind(query);
if (i != _peopleCache.cend()) {
@@ -104,7 +105,12 @@ bool ShareBox::onSearchByUsername(bool searchCache) {
} else if (_peopleQuery != query) {
_peopleQuery = query;
_peopleFull = false;
_peopleRequest = MTP::send(MTPcontacts_Search(MTP_string(_peopleQuery), MTP_int(SearchPeopleLimit)), rpcDone(&ShareBox::peopleReceived), rpcFail(&ShareBox::peopleFailed));
_peopleRequest = MTP::send(
MTPcontacts_Search(
MTP_string(_peopleQuery),
MTP_int(SearchPeopleLimit)),
rpcDone(&ShareBox::peopleReceived),
rpcFail(&ShareBox::peopleFailed));
_peopleQueries.insert(_peopleRequest, _peopleQuery);
}
}
@@ -117,7 +123,11 @@ void ShareBox::onNeedSearchByUsername() {
}
}
void ShareBox::peopleReceived(const MTPcontacts_Found &result, mtpRequestId requestId) {
void ShareBox::peopleReceived(
const MTPcontacts_Found &result,
mtpRequestId requestId) {
Expects(result.type() == mtpc_contacts_found);
auto query = _peopleQuery;
auto i = _peopleQueries.find(requestId);
@@ -133,7 +143,10 @@ void ShareBox::peopleReceived(const MTPcontacts_Found &result, mtpRequestId requ
auto &found = result.c_contacts_found();
App::feedUsers(found.vusers);
App::feedChats(found.vchats);
_inner->peopleReceived(query, found.vresults.v);
_inner->peopleReceived(
query,
found.vmy_results.v,
found.vresults.v);
} break;
}
@@ -273,16 +286,18 @@ ShareBox::Inner::Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallbac
_rowHeight = st::shareRowHeight;
setAttribute(Qt::WA_OpaquePaintEvent);
auto dialogs = App::main()->dialogsList();
if (auto self = App::self()) {
const auto dialogs = App::main()->dialogsList();
if (const auto self = App::self()) {
if (_filterCallback(App::self())) {
_chatsIndexed->addToEnd(App::history(self));
}
}
for_const (auto row, dialogs->all()) {
auto history = row->history();
if (!history->peer->isSelf() && _filterCallback(history->peer)) {
_chatsIndexed->addToEnd(history);
for (const auto row : dialogs->all()) {
if (const auto history = row->history()) {
if (!history->peer->isSelf()
&& _filterCallback(history->peer)) {
_chatsIndexed->addToEnd(history);
}
}
}
@@ -347,7 +362,9 @@ void ShareBox::Inner::activateSkipPage(int pageHeight, int direction) {
void ShareBox::Inner::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.flags & Notify::PeerUpdate::Flag::NameChanged) {
_chatsIndexed->peerNameChanged(update.peer, update.oldNameFirstChars);
_chatsIndexed->peerNameChanged(
update.peer,
update.oldNameFirstLetters);
}
updateChat(update.peer);
@@ -402,20 +419,24 @@ void ShareBox::Inner::repaintChat(not_null<PeerData*> peer) {
int ShareBox::Inner::chatIndex(not_null<PeerData*> peer) const {
int index = 0;
if (_filter.isEmpty()) {
for_const (auto row, _chatsIndexed->all()) {
if (row->history()->peer == peer) {
return index;
for (const auto row : _chatsIndexed->all()) {
if (const auto history = row->history()) {
if (history->peer == peer) {
return index;
}
}
++index;
}
} else {
for_const (auto row, _filtered) {
if (row->history()->peer == peer) {
return index;
for (const auto row : _filtered) {
if (const auto history = row->history()) {
if (history->peer == peer) {
return index;
}
}
++index;
}
for_const (auto row, d_byUsernameFiltered) {
for (const auto row : d_byUsernameFiltered) {
if (row->peer == peer) {
return index;
}
@@ -448,7 +469,7 @@ void ShareBox::Inner::loadProfilePhotos(int yFrom) {
if (((*i)->pos() * _rowHeight) >= yTo) {
break;
}
(*i)->history()->peer->loadUserpic();
(*i)->entry()->loadUserpic();
}
}
} else if (!_filtered.isEmpty()) {
@@ -459,13 +480,15 @@ void ShareBox::Inner::loadProfilePhotos(int yFrom) {
if (to > _filtered.size()) to = _filtered.size();
for (; from < to; ++from) {
_filtered[from]->history()->peer->loadUserpic();
_filtered[from]->entry()->loadUserpic();
}
}
}
}
ShareBox::Inner::Chat *ShareBox::Inner::getChat(Dialogs::Row *row) {
Expects(row->history() != nullptr);
auto data = static_cast<Chat*>(row->attached);
if (!data) {
auto peer = row->history()->peer;
@@ -555,7 +578,7 @@ void ShareBox::Inner::paintEvent(QPaintEvent *e) {
p.setPen(st::noContactsColor);
}
} else {
if (_filtered.isEmpty() && _byUsernameFiltered.isEmpty()) {
if (_filtered.isEmpty() && _byUsernameFiltered.empty()) {
// empty
p.setFont(st::noContactsFont);
p.setPen(st::noContactsColor);
@@ -573,7 +596,7 @@ void ShareBox::Inner::paintEvent(QPaintEvent *e) {
indexFrom -= filteredSize;
indexTo -= filteredSize;
}
if (!_byUsernameFiltered.isEmpty()) {
if (!_byUsernameFiltered.empty()) {
if (indexFrom < 0) indexFrom = 0;
while (indexFrom < indexTo) {
if (indexFrom >= d_byUsernameFiltered.size()) {
@@ -638,16 +661,17 @@ void ShareBox::Inner::changeCheckState(Chat *chat) {
if (!chat) return;
if (!_filter.isEmpty()) {
auto row = _chatsIndexed->getRow(chat->peer->id);
const auto history = App::history(chat->peer);
auto row = _chatsIndexed->getRow(history);
if (!row) {
auto rowsByLetter = _chatsIndexed->addToEnd(App::history(chat->peer));
auto it = rowsByLetter.find(0);
const auto rowsByLetter = _chatsIndexed->addToEnd(history);
const auto it = rowsByLetter.find(0);
Assert(it != rowsByLetter.cend());
row = it->second;
}
chat = getChat(row);
if (!chat->checkbox.checked()) {
_chatsIndexed->moveToTop(chat->peer);
_chatsIndexed->moveToTop(history);
}
}
@@ -721,8 +745,8 @@ void ShareBox::Inner::updateFilter(QString filter) {
}
if (toFilter) {
_filtered.reserve(toFilter->size());
for_const (auto row, *toFilter) {
auto &nameWords = row->history()->peer->nameWords();
for (const auto row : *toFilter) {
auto &nameWords = row->entry()->chatsListNameWords();
auto nb = nameWords.cbegin(), ne = nameWords.cend(), ni = nb;
for (fi = fb; fi != fe; ++fi) {
auto filterName = *fi;
@@ -752,32 +776,36 @@ void ShareBox::Inner::updateFilter(QString filter) {
}
}
void ShareBox::Inner::peopleReceived(const QString &query, const QVector<MTPPeer> &people) {
void ShareBox::Inner::peopleReceived(
const QString &query,
const QVector<MTPPeer> &my,
const QVector<MTPPeer> &people) {
_lastQuery = query.toLower().trimmed();
if (_lastQuery.at(0) == '@') _lastQuery = _lastQuery.mid(1);
int32 already = _byUsernameFiltered.size();
_byUsernameFiltered.reserve(already + people.size());
d_byUsernameFiltered.reserve(already + people.size());
for_const (auto &mtpPeer, people) {
auto peerId = peerFromMTP(mtpPeer);
int j = 0;
for (; j < already; ++j) {
if (_byUsernameFiltered[j]->id == peerId) break;
}
if (j == already) {
auto *peer = App::peer(peerId);
if (!peer || !_filterCallback(peer)) continue;
auto chat = new Chat(peer, [this, peer] { repaintChat(peer); });
updateChatName(chat, peer);
if (auto row = _chatsIndexed->getRow(peer->id)) {
continue;
_byUsernameFiltered.reserve(already + my.size() + people.size());
d_byUsernameFiltered.reserve(already + my.size() + people.size());
const auto feedList = [&](const QVector<MTPPeer> &list) {
for (const auto &mtpPeer : list) {
if (const auto peer = App::peerLoaded(peerFromMTP(mtpPeer))) {
const auto history = App::historyLoaded(peer);
if (!_filterCallback(peer)) {
continue;
} else if (history && _chatsIndexed->getRow(history)) {
continue;
} else if (base::contains(_byUsernameFiltered, peer)) {
continue;
}
auto chat = new Chat(peer, [=] { repaintChat(peer); });
updateChatName(chat, peer);
_byUsernameFiltered.push_back(peer);
d_byUsernameFiltered.push_back(chat);
}
_byUsernameFiltered.push_back(peer);
d_byUsernameFiltered.push_back(chat);
}
}
};
feedList(my);
feedList(people);
_searching = false;
refresh();
}

View File

@@ -65,7 +65,9 @@ private:
void addPeerToMultiSelect(PeerData *peer, bool skipAnimation = false);
void onPeerSelectedChanged(PeerData *peer, bool checked);
void peopleReceived(const MTPcontacts_Found &result, mtpRequestId requestId);
void peopleReceived(
const MTPcontacts_Found &result,
mtpRequestId requestId);
bool peopleFailed(const RPCError &error, mtpRequestId requestId);
CopyCallback _copyCallback;
@@ -107,7 +109,10 @@ public:
QVector<PeerData*> selected() const;
bool hasSelected() const;
void peopleReceived(const QString &query, const QVector<MTPPeer> &people);
void peopleReceived(
const QString &query,
const QVector<MTPPeer> &my,
const QVector<MTPPeer> &people);
void activateSkipRow(int direction);
void activateSkipColumn(int direction);
@@ -202,9 +207,7 @@ private:
bool _searching = false;
QString _lastQuery;
using ByUsernameRows = QVector<PeerData*>;
using ByUsernameDatas = QVector<Chat*>;
ByUsernameRows _byUsernameFiltered;
ByUsernameDatas d_byUsernameFiltered;
std::vector<PeerData*> _byUsernameFiltered;
std::vector<Chat*> d_byUsernameFiltered;
};

View File

@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/sticker_set_box.h"
#include "data/data_document.h"
#include "data/data_session.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "mainwindow.h"
@@ -121,8 +122,8 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
_pack.reserve(v.size());
_packOvers.reserve(v.size());
for (int i = 0, l = v.size(); i < l; ++i) {
auto doc = App::feedDocument(v.at(i));
if (!doc || !doc->sticker()) continue;
auto doc = Auth().data().document(v.at(i));
if (!doc->sticker()) continue;
_pack.push_back(doc);
_packOvers.push_back(Animation());
@@ -138,7 +139,7 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
Stickers::Pack p;
p.reserve(stickers.size());
for (auto j = 0, c = stickers.size(); j != c; ++j) {
auto doc = App::document(stickers[j].v);
auto doc = Auth().data().document(stickers[j].v);
if (!doc || !doc->sticker()) continue;
p.push_back(doc);
@@ -155,12 +156,16 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
_setCount = s.vcount.v;
_setHash = s.vhash.v;
_setFlags = s.vflags.v;
_setInstallDate = s.has_installed_date()
? s.vinstalled_date.v
: TimeId(0);
auto &sets = Auth().data().stickerSetsRef();
auto it = sets.find(_setId);
if (it != sets.cend()) {
auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_not_loaded | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_special);
_setFlags |= clientFlags;
it->flags = _setFlags;
it->installDate = _setInstallDate;
it->stickers = _pack;
it->emoji = _emoji;
}
@@ -200,13 +205,25 @@ void StickerSetBox::Inner::installDone(const MTPmessages_StickerSetInstallResult
Auth().data().archivedStickerSetsOrderRef().removeAt(index);
}
}
_setInstallDate = unixtime();
_setFlags &= ~MTPDstickerSet::Flag::f_archived;
_setFlags |= MTPDstickerSet::Flag::f_installed;
_setFlags |= MTPDstickerSet::Flag::f_installed_date;
auto it = sets.find(_setId);
if (it == sets.cend()) {
it = sets.insert(_setId, Stickers::Set(_setId, _setAccess, _setTitle, _setShortName, _setCount, _setHash, _setFlags));
it = sets.insert(
_setId,
Stickers::Set(
_setId,
_setAccess,
_setTitle,
_setShortName,
_setCount,
_setHash,
_setFlags,
_setInstallDate));
} else {
it->flags = _setFlags;
it->installDate = _setInstallDate;
}
it->stickers = _pack;
it->emoji = _emoji;
@@ -238,7 +255,7 @@ void StickerSetBox::Inner::installDone(const MTPmessages_StickerSetInstallResult
Local::writeArchivedStickers();
}
Local::writeInstalledStickers();
Auth().data().markStickersUpdated();
Auth().data().notifyStickersUpdated();
}
_setInstalled.fire_copy(_setId);
}
@@ -395,11 +412,17 @@ bool StickerSetBox::Inner::loaded() const {
return _loaded && !_pack.isEmpty();
}
int32 StickerSetBox::Inner::notInstalled() const {
if (!_loaded) return 0;
auto it = Auth().data().stickerSets().constFind(_setId);
if (it == Auth().data().stickerSets().cend() || !(it->flags & MTPDstickerSet::Flag::f_installed) || (it->flags & MTPDstickerSet::Flag::f_archived)) return _pack.size();
return 0;
bool StickerSetBox::Inner::notInstalled() const {
if (!_loaded) {
return false;
}
const auto it = Auth().data().stickerSets().constFind(_setId);
if ((it == Auth().data().stickerSets().cend())
|| !(it->flags & MTPDstickerSet::Flag::f_installed_date)
|| (it->flags & MTPDstickerSet::Flag::f_archived)) {
return _pack.size() > 0;
}
return false;
}
bool StickerSetBox::Inner::official() const {

View File

@@ -50,7 +50,7 @@ public:
Inner(QWidget *parent, const MTPInputStickerSet &set);
bool loaded() const;
int32 notInstalled() const;
bool notInstalled() const;
bool official() const;
base::lambda<TextWithEntities()> title() const;
QString shortName() const;
@@ -98,9 +98,10 @@ private:
uint64 _setId = 0;
uint64 _setAccess = 0;
QString _setTitle, _setShortName;
int32 _setCount = 0;
int _setCount = 0;
int32 _setHash = 0;
MTPDstickerSet::Flags _setFlags = 0;
TimeId _setInstallDate = TimeId(0);
MTPInputStickerSet _input;

View File

@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "stickers_box.h"
#include "data/data_document.h"
#include "data/data_session.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "chat_helpers/stickers.h"
@@ -162,8 +163,7 @@ void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedSti
auto addedSet = false;
auto changedSets = false;
auto &v = stickers.vsets.v;
for_const (auto &stickerSet, v) {
for_const (const auto &stickerSet, stickers.vsets.v) {
const MTPDstickerSet *setData = nullptr;
switch (stickerSet.type()) {
case mtpc_stickerSetCovered: {
@@ -201,7 +201,8 @@ void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedSti
if (addedSet) {
_archived.widget()->updateSize();
} else {
_allArchivedLoaded = v.isEmpty() || (!changedSets && offsetId != 0);
_allArchivedLoaded = stickers.vsets.v.isEmpty()
|| (!changedSets && offsetId != 0);
if (changedSets) {
loadMoreArchived();
}
@@ -445,8 +446,14 @@ void StickersBox::installSet(uint64 setId) {
if (_featured.widget()) _featured.widget()->setRemovedSets(_localRemoved);
_archived.widget()->setRemovedSets(_localRemoved);
}
if (!(it->flags & MTPDstickerSet::Flag::f_installed) || (it->flags & MTPDstickerSet::Flag::f_archived)) {
MTP::send(MTPmessages_InstallStickerSet(Stickers::inputSetId(*it), MTP_boolFalse()), rpcDone(&StickersBox::installDone), rpcFail(&StickersBox::installFail, setId));
if (!(it->flags & MTPDstickerSet::Flag::f_installed_date)
|| (it->flags & MTPDstickerSet::Flag::f_archived)) {
MTP::send(
MTPmessages_InstallStickerSet(
Stickers::inputSetId(*it),
MTP_boolFalse()),
rpcDone(&StickersBox::installDone),
rpcFail(&StickersBox::installFail, setId));
Stickers::InstallLocally(setId);
}
@@ -454,7 +461,8 @@ void StickersBox::installSet(uint64 setId) {
void StickersBox::installDone(const MTPmessages_StickerSetInstallResult &result) {
if (result.type() == mtpc_messages_stickerSetInstallResultArchive) {
Stickers::ApplyArchivedResult(result.c_messages_stickerSetInstallResultArchive());
Stickers::ApplyArchivedResult(
result.c_messages_stickerSetInstallResultArchive());
}
}
@@ -1563,8 +1571,13 @@ QString StickersBox::Inner::fillSetTitle(const Stickers::Set &set, int maxNameWi
return result;
}
void StickersBox::Inner::fillSetFlags(const Stickers::Set &set, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outArchived) {
*outInstalled = (set.flags & MTPDstickerSet::Flag::f_installed);
void StickersBox::Inner::fillSetFlags(
const Stickers::Set &set,
bool *outInstalled,
bool *outOfficial,
bool *outUnread,
bool *outArchived) {
*outInstalled = (set.flags & MTPDstickerSet::Flag::f_installed_date);
*outOfficial = (set.flags & MTPDstickerSet::Flag::f_official);
*outArchived = (set.flags & MTPDstickerSet::Flag::f_archived);
if (_section == Section::Featured) {

View File

@@ -17,6 +17,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_boxes.h"
#include "messenger.h"
namespace {
constexpr auto kMinUsernameLength = 5;
} // namespace
UsernameBox::UsernameBox(QWidget*)
: _username(this, st::defaultInputField, [] { return qsl("@username"); }, App::self()->username, false)
, _link(this, QString(), st::boxLinkButton)
@@ -102,9 +108,13 @@ void UsernameBox::onCheck() {
MTP::cancel(_checkRequestId);
}
QString name = getName();
if (name.size() >= MinUsernameLength) {
if (name.size() >= kMinUsernameLength) {
_checkUsername = name;
_checkRequestId = MTP::send(MTPaccount_CheckUsername(MTP_string(name)), rpcDone(&UsernameBox::onCheckDone), rpcFail(&UsernameBox::onCheckFail));
_checkRequestId = MTP::send(
MTPaccount_CheckUsername(
MTP_string(name)),
rpcDone(&UsernameBox::onCheckDone),
rpcFail(&UsernameBox::onCheckFail));
}
}
@@ -130,7 +140,7 @@ void UsernameBox::onChanged() {
return;
}
}
if (name.size() < MinUsernameLength) {
if (name.size() < kMinUsernameLength) {
if (_errorText != lang(lng_username_too_short)) {
_errorText = lang(lng_username_too_short);
update();

View File

@@ -13,9 +13,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "observer_peer.h"
#include "ui/effects/ripple_animation.h"
#include "calls/calls_instance.h"
#include "history/history_media_types.h"
#include "history/history.h"
#include "history/history_item.h"
#include "mainwidget.h"
#include "auth_session.h"
#include "data/data_session.h"
#include "data/data_media_types.h"
namespace Calls {
namespace {
@@ -36,7 +39,8 @@ public:
};
bool canAddItem(not_null<const HistoryItem*> item) const {
return (ComputeType(item) == _type && item->date.date() == _date);
return (ComputeType(item) == _type)
&& (ItemDateTime(item).date() == _date);
}
void addItem(not_null<HistoryItem*> item) {
Expects(canAddItem(item));
@@ -114,7 +118,7 @@ private:
BoxController::Row::Row(not_null<HistoryItem*> item)
: PeerListRow(item->history()->peer, item->id)
, _items(1, item)
, _date(item->date.date())
, _date(ItemDateTime(item).date())
, _type(ComputeType(item)) {
refreshStatus();
}
@@ -159,7 +163,7 @@ void BoxController::Row::refreshStatus() {
return;
}
auto text = [this] {
auto time = _items.front()->date.time().toString(cTimeFormat());
auto time = ItemDateTime(_items.front()).time().toString(cTimeFormat());
auto today = QDateTime::currentDateTime().date();
if (_date == today) {
return lng_call_box_status_today(lt_time, time);
@@ -175,10 +179,11 @@ BoxController::Row::Type BoxController::Row::ComputeType(
not_null<const HistoryItem*> item) {
if (item->out()) {
return Type::Out;
} else if (auto media = item->getMedia()) {
if (media->type() == MediaTypeCall) {
auto reason = static_cast<HistoryCall*>(media)->reason();
if (reason == HistoryCall::FinishReason::Busy || reason == HistoryCall::FinishReason::Missed) {
} else if (auto media = item->media()) {
if (const auto call = media->call()) {
const auto reason = call->finishReason;
if (reason == Data::Call::FinishReason::Busy
|| reason == Data::Call::FinishReason::Missed) {
return Type::Missed;
}
}
@@ -244,6 +249,7 @@ void BoxController::loadMoreRows() {
MTP_int(0),
MTP_int(_offsetId ? kFirstPageCount : kPerPageCount),
MTP_int(0),
MTP_int(0),
MTP_int(0)
)).done([this](const MTPmessages_Messages &result) {
_loadRequestId = 0;

View File

@@ -248,7 +248,10 @@ QString Call::getDebugLog() const {
void Call::startWaitingTrack() {
_waitingTrack = Media::Audio::Current().createTrack();
auto trackFileName = Auth().data().getSoundPath((_type == Type::Outgoing) ? qsl("call_outgoing") : qsl("call_incoming"));
auto trackFileName = Auth().settings().getSoundPath(
(_type == Type::Outgoing)
? qsl("call_outgoing")
: qsl("call_incoming"));
_waitingTrack->samplePeakEach(kSoundSampleMs);
_waitingTrack->fillFromFile(trackFileName);
_waitingTrack->playInLoop();

View File

@@ -60,7 +60,8 @@ void Instance::playSound(Sound sound) {
case Sound::Busy: {
if (!_callBusyTrack) {
_callBusyTrack = Media::Audio::Current().createTrack();
_callBusyTrack->fillFromFile(Auth().data().getSoundPath(qsl("call_busy")));
_callBusyTrack->fillFromFile(
Auth().settings().getSoundPath(qsl("call_busy")));
}
_callBusyTrack->playOnce();
} break;
@@ -68,7 +69,8 @@ void Instance::playSound(Sound sound) {
case Sound::Ended: {
if (!_callEndedTrack) {
_callEndedTrack = Media::Audio::Current().createTrack();
_callEndedTrack->fillFromFile(Auth().data().getSoundPath(qsl("call_end")));
_callEndedTrack->fillFromFile(
Auth().settings().getSoundPath(qsl("call_end")));
}
_callEndedTrack->playOnce();
} break;
@@ -76,7 +78,8 @@ void Instance::playSound(Sound sound) {
case Sound::Connecting: {
if (!_callConnectingTrack) {
_callConnectingTrack = Media::Audio::Current().createTrack();
_callConnectingTrack->fillFromFile(Auth().data().getSoundPath(qsl("call_connect")));
_callConnectingTrack->fillFromFile(
Auth().settings().getSoundPath(qsl("call_connect")));
}
_callConnectingTrack->playOnce();
} break;

View File

@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_panel.h"
#include "data/data_photo.h"
#include "data/data_session.h"
#include "calls/calls_emoji_fingerprint.h"
#include "styles/style_calls.h"
#include "styles/style_history.h"
@@ -25,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "observer_peer.h"
#include "platform/platform_specific.h"
#include "window/main_window.h"
#include "layout.h"
namespace Calls {
namespace {
@@ -429,7 +431,7 @@ void Panel::processUserPhoto() {
_user->loadUserpic(true);
}
const auto photo = _user->userpicPhotoId()
? App::photo(_user->userpicPhotoId())
? Auth().data().photo(_user->userpicPhotoId()).get()
: nullptr;
if (isGoodUserPhoto(photo)) {
photo->full->load(true);
@@ -441,7 +443,7 @@ void Panel::processUserPhoto() {
void Panel::refreshUserPhoto() {
const auto photo = _user->userpicPhotoId()
? App::photo(_user->userpicPhotoId())
? Auth().data().photo(_user->userpicPhotoId()).get()
: nullptr;
const auto isNewPhoto = [&](not_null<PhotoData*> photo) {
return photo->full->loaded()

View File

@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "observer_peer.h"
#include "boxes/abstract_box.h"
#include "base/timer.h"
#include "layout.h"
namespace Calls {
namespace {

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "chat_helpers/bot_keyboard.h"
#include "history/history.h"
#include "history/history_item_components.h"
#include "styles/style_widgets.h"
#include "styles/style_history.h"
@@ -279,7 +280,7 @@ void BotKeyboard::updateSelected() {
auto p = mapFromGlobal(_lastMousePos);
auto x = rtl() ? st::botKbScroll.width : _st->margin;
auto link = _impl->getState(p - QPoint(x, _st->margin));
auto link = _impl->getLink(p - QPoint(x, _st->margin));
if (ClickHandler::setActive(link, this)) {
Ui::Tooltip::Hide();
setCursor(link ? style::cur_pointer : style::cur_default);

View File

@@ -9,14 +9,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/tooltip.h"
namespace style {
struct BotKeyboardButton;
} // namespace style
class ReplyKeyboard;
class BotKeyboard
: public TWidget
, public Ui::AbstractTooltipShower
, public ClickHandlerHost {
Q_OBJECT
public:
BotKeyboard(QWidget *parent);

View File

@@ -21,17 +21,17 @@ stickersRestrictedLabel: FlatLabel(defaultFlatLabel) {
textFg: noContactsColor;
}
stickersTrendingHeader: 45px;
stickersTrendingSkip: 15px;
stickersTrendingHeader: 56px;
stickersTrendingSkip: 4px;
stickersTrendingHeaderFont: semiboldFont;
stickersTrendingHeaderFg: windowFg;
stickersTrendingHeaderTop: 0px;
stickersTrendingHeaderTop: 11px;
stickersTrendingSubheaderFont: normalFont;
stickersTrendingSubheaderFg: windowSubTextFg;
stickersTrendingSubheaderTop: 20px;
stickersTrendingSubheaderTop: 31px;
stickersTrendingAddTop: 3px;
stickersTrendingAddTop: 14px;
stickersTrendingAdd: RoundButton(defaultActiveButton) {
width: -16px;
height: 26px;
@@ -91,6 +91,7 @@ stickersRowDuration: 200;
stickersSettings: icon {{ "emoji_settings", emojiIconFg }};
stickersTrending: icon {{ "emoji_trending", emojiIconFg }};
stickersFaved: icon {{ "emoji_faved", emojiIconFg }};
stickersSearch: icon {{ "title_search", emojiIconFg, point(0px, 5px) }};
stickersSettingsUnreadSize: 17px;
stickersSettingsUnreadPosition: point(4px, 5px);
@@ -209,6 +210,8 @@ stickerGroupCategoryAdd: stickersTrendingAdd;
stickersToastMaxWidth: 340px;
stickersToastPadding: margins(16px, 13px, 16px, 12px);
stickersEmpty: icon {{ "stickers_empty", windowSubTextFg }};
inlineBotsScroll: ScrollArea(defaultSolidScroll) {
deltat: stickerPanPadding;
deltab: stickerPanPadding;

View File

@@ -101,7 +101,7 @@ void FieldAutocomplete::showStickers(EmojiPtr emoji) {
_emoji = emoji;
_type = Type::Stickers;
if (!emoji) {
rowsUpdated(_mrows, _hrows, _brows, Stickers::Pack(), false);
rowsUpdated(_mrows, _hrows, _brows, internal::StickerRows(), false);
return;
}
@@ -135,9 +135,9 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
internal::MentionRows mrows;
internal::HashtagRows hrows;
internal::BotCommandRows brows;
Stickers::Pack srows;
internal::StickerRows srows;
if (_emoji) {
srows = Stickers::GetListByEmoji(_emoji);
srows = Stickers::GetListByEmoji(_emoji, _stickersSeed);
} else if (_type == Type::Mentions) {
int maxListSize = _addInlineBots ? cRecentInlineBots().size() : 0;
if (_chat) {
@@ -159,8 +159,8 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
}
return true;
};
auto filterNotPassedByName = [this, &filterNotPassedByUsername](UserData *user) -> bool {
for (auto &nameWord : user->nameWords()) {
auto filterNotPassedByName = [&](UserData *user) -> bool {
for (const auto &nameWord : user->nameWords()) {
if (nameWord.startsWith(_filter, Qt::CaseInsensitive)) {
auto exactUsername = (user->username.compare(_filter, Qt::CaseInsensitive) == 0);
return exactUsername;
@@ -319,11 +319,12 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
_inner->setRecentInlineBotsInRows(recentInlineBots);
}
void FieldAutocomplete::rowsUpdated(const internal::MentionRows &mrows, const internal::HashtagRows &hrows, const internal::BotCommandRows &brows, const Stickers::Pack &srows, bool resetScroll) {
if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty() && srows.isEmpty()) {
void FieldAutocomplete::rowsUpdated(const internal::MentionRows &mrows, const internal::HashtagRows &hrows, const internal::BotCommandRows &brows, const internal::StickerRows &srows, bool resetScroll) {
if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty() && srows.empty()) {
if (!isHidden()) {
hideAnimated();
}
_scroll->scrollToY(0);
_mrows.clear();
_hrows.clear();
_brows.clear();
@@ -355,7 +356,7 @@ void FieldAutocomplete::setBoundings(QRect boundings) {
void FieldAutocomplete::recount(bool resetScroll) {
int32 h = 0, oldst = _scroll->scrollTop(), st = oldst, maxh = 4.5 * st::mentionHeight;
if (!_srows.isEmpty()) {
if (!_srows.empty()) {
int32 stickersPerRow = qMax(1, int32(_boundings.width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width()));
int32 rows = rowscount(_srows.size(), stickersPerRow);
h = st::stickerPanPadding + rows * st::stickerPanSize.height();
@@ -415,6 +416,7 @@ void FieldAutocomplete::showAnimated() {
return;
}
if (_cache.isNull()) {
_stickersSeed = rand_value<uint64>();
_scroll->show();
_cache = Ui::GrabWidget(this);
}
@@ -476,7 +478,7 @@ bool FieldAutocomplete::eventFilter(QObject *obj, QEvent *e) {
QKeyEvent *ev = static_cast<QKeyEvent*>(e);
if (!(ev->modifiers() & (Qt::AltModifier | Qt::ControlModifier | Qt::ShiftModifier | Qt::MetaModifier))) {
if (!hidden) {
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down || (!_srows.isEmpty() && (ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right))) {
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down || (!_srows.empty() && (ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right))) {
return _inner->moveSel(ev->key());
} else if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
return _inner->chooseSelected(ChooseMethod::ByEnter);
@@ -497,7 +499,7 @@ FieldAutocomplete::~FieldAutocomplete() {
namespace internal {
FieldAutocompleteInner::FieldAutocompleteInner(FieldAutocomplete *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, Stickers::Pack *srows)
FieldAutocompleteInner::FieldAutocompleteInner(FieldAutocomplete *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, StickerRows *srows)
: _parent(parent)
, _mrows(mrows)
, _hrows(hrows)
@@ -526,7 +528,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
int32 mentionwidth = width() - mentionleft - 2 * st::mentionPadding.right();
int32 htagleft = st::historyAttach.width + st::historyComposeField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width;
if (!_srows->isEmpty()) {
if (!_srows->empty()) {
int32 rows = rowscount(_srows->size(), _stickersPerRow);
int32 fromrow = floorclamp(r.y() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows);
int32 torow = ceilclamp(r.y() + r.height() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows);
@@ -696,7 +698,7 @@ bool FieldAutocompleteInner::moveSel(int key) {
_mouseSel = false;
int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? (_brows->isEmpty() ? _srows->size() : _brows->size()) : _hrows->size()) : _mrows->size());
int32 direction = (key == Qt::Key_Up) ? -1 : (key == Qt::Key_Down ? 1 : 0);
if (!_srows->isEmpty()) {
if (!_srows->empty()) {
if (key == Qt::Key_Left) {
direction = -1;
} else if (key == Qt::Key_Right) {
@@ -720,7 +722,7 @@ bool FieldAutocompleteInner::moveSel(int key) {
}
bool FieldAutocompleteInner::chooseSelected(FieldAutocomplete::ChooseMethod method) const {
if (!_srows->isEmpty()) {
if (!_srows->empty()) {
if (_sel >= 0 && _sel < _srows->size()) {
emit stickerChosen(_srows->at(_sel), method);
return true;
@@ -790,7 +792,7 @@ void FieldAutocompleteInner::mousePressEvent(QMouseEvent *e) {
_mouseSel = true;
onUpdateSelected(true);
} else if (_srows->isEmpty()) {
} else if (_srows->empty()) {
chooseSelected(FieldAutocomplete::ChooseMethod::ByClick);
} else {
_down = _sel;
@@ -814,7 +816,7 @@ void FieldAutocompleteInner::mouseReleaseEvent(QMouseEvent *e) {
return;
}
if (_sel < 0 || _sel != pressed || _srows->isEmpty()) return;
if (_sel < 0 || _sel != pressed || _srows->empty()) return;
chooseSelected(FieldAutocomplete::ChooseMethod::ByClick);
}
@@ -834,7 +836,7 @@ void FieldAutocompleteInner::leaveEventHook(QEvent *e) {
void FieldAutocompleteInner::updateSelectedRow() {
if (_sel >= 0) {
if (_srows->isEmpty()) {
if (_srows->empty()) {
update(0, _sel * st::mentionHeight, width(), st::mentionHeight);
} else {
int32 row = _sel / _stickersPerRow, col = _sel % _stickersPerRow;
@@ -849,7 +851,7 @@ void FieldAutocompleteInner::setSel(int sel, bool scroll) {
updateSelectedRow();
if (scroll && _sel >= 0) {
if (_srows->isEmpty()) {
if (_srows->empty()) {
emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight);
} else {
int32 row = _sel / _stickersPerRow;
@@ -865,7 +867,7 @@ void FieldAutocompleteInner::onUpdateSelected(bool force) {
if (_down >= 0 && !_previewShown) return;
int32 sel = -1, maxSel = 0;
if (!_srows->isEmpty()) {
if (!_srows->empty()) {
int32 rows = rowscount(_srows->size(), _stickersPerRow);
int32 row = (mouse.y() >= st::stickerPanPadding) ? ((mouse.y() - st::stickerPanPadding) / st::stickerPanSize.height()) : -1;
int32 col = (mouse.x() >= st::stickerPanPadding) ? ((mouse.x() - st::stickerPanPadding) / st::stickerPanSize.width()) : -1;

View File

@@ -19,6 +19,7 @@ namespace internal {
using MentionRows = QList<UserData*>;
using HashtagRows = QList<QString>;
using BotCommandRows = QList<QPair<UserData*, const BotCommand*>>;
using StickerRows = std::vector<not_null<DocumentData*>>;
class FieldAutocompleteInner;
@@ -53,7 +54,7 @@ public:
bool chooseSelected(ChooseMethod method) const;
bool stickersShown() const {
return !_srows.isEmpty();
return !_srows.empty();
}
bool overlaps(const QRect &globalRect) {
@@ -92,9 +93,9 @@ private:
internal::MentionRows _mrows;
internal::HashtagRows _hrows;
internal::BotCommandRows _brows;
Stickers::Pack _srows;
internal::StickerRows _srows;
void rowsUpdated(const internal::MentionRows &mrows, const internal::HashtagRows &hrows, const internal::BotCommandRows &brows, const Stickers::Pack &srows, bool resetScroll);
void rowsUpdated(const internal::MentionRows &mrows, const internal::HashtagRows &hrows, const internal::BotCommandRows &brows, const internal::StickerRows &srows, bool resetScroll);
object_ptr<Ui::ScrollArea> _scroll;
QPointer<internal::FieldAutocompleteInner> _inner;
@@ -103,6 +104,7 @@ private:
UserData *_user = nullptr;
ChannelData *_channel = nullptr;
EmojiPtr _emoji;
uint64 _stickersSeed = 0;
enum class Type {
Mentions,
Hashtags,
@@ -129,7 +131,7 @@ class FieldAutocompleteInner final : public TWidget, private base::Subscriber {
Q_OBJECT
public:
FieldAutocompleteInner(FieldAutocomplete *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, Stickers::Pack *srows);
FieldAutocompleteInner(FieldAutocomplete *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, StickerRows *srows);
void clearSel(bool hidden = false);
bool moveSel(int key);
@@ -167,7 +169,7 @@ private:
MentionRows *_mrows;
HashtagRows *_hrows;
BotCommandRows *_brows;
Stickers::Pack *_srows;
StickerRows *_srows;
int32 _stickersPerRow, _recentInlineBotsInRows;
int32 _sel, _down;
bool _mouseSel;

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo.h"
#include "data/data_document.h"
#include "data/data_session.h"
#include "styles/style_chat_helpers.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
@@ -20,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "mainwindow.h"
#include "window/window_controller.h"
#include "history/view/history_view_cursor_state.h"
namespace ChatHelpers {
namespace {
@@ -207,9 +209,10 @@ void GifsListWidget::inlineResultsDone(const MTPmessages_BotResults &result) {
auto it = _inlineCache.find(_inlineQuery);
auto adding = (it != _inlineCache.cend());
// #TODO layer 72 feed users
if (result.type() == mtpc_messages_botResults) {
auto &d = result.c_messages_botResults();
App::feedUsers(d.vusers);
auto &v = d.vresults.v;
auto queryId = d.vquery_id.v;
@@ -885,7 +888,7 @@ void GifsListWidget::updateSelected() {
int row = -1, col = -1, sel = -1;
ClickHandlerPtr lnk;
ClickHandlerHost *lnkhost = nullptr;
HistoryCursorState cursor = HistoryDefaultCursorState;
HistoryView::CursorState cursor = HistoryView::CursorState::None;
if (sy >= 0) {
row = 0;
for (int rows = _rows.size(); row < rows; ++row) {
@@ -910,10 +913,12 @@ void GifsListWidget::updateSelected() {
}
if (col < inlineItems.size()) {
sel = row * MatrixRowShift + col;
auto result = inlineItems[col]->getState(QPoint(sx, sy), HistoryStateRequest());
auto result = inlineItems[col]->getState(
QPoint(sx, sy),
HistoryView::StateRequest());
lnk = result.link;
cursor = result.cursor;
lnkhost = inlineItems.at(col);
lnkhost = inlineItems[col];
} else {
row = col = -1;
}

View File

@@ -81,7 +81,8 @@ TextWithTags::Tags ConvertEntitiesToTextTags(const EntitiesInText &entities) {
return result;
}
std::unique_ptr<QMimeData> MimeDataFromTextWithEntities(const TextWithEntities &forClipboard) {
std::unique_ptr<QMimeData> MimeDataFromTextWithEntities(
const TextWithEntities &forClipboard) {
if (forClipboard.text.isEmpty()) {
return nullptr;
}
@@ -93,11 +94,21 @@ std::unique_ptr<QMimeData> MimeDataFromTextWithEntities(const TextWithEntities &
for (auto &tag : tags) {
tag.id = ConvertTagToMimeTag(tag.id);
}
result->setData(Ui::FlatTextarea::tagsMimeType(), Ui::FlatTextarea::serializeTagsList(tags));
result->setData(
Ui::FlatTextarea::tagsMimeType(),
Ui::FlatTextarea::serializeTagsList(tags));
}
return result;
}
void SetClipboardWithEntities(
const TextWithEntities &forClipboard,
QClipboard::Mode mode) {
if (auto data = MimeDataFromTextWithEntities(forClipboard)) {
QApplication::clipboard()->setMimeData(data.release(), mode);
}
}
MessageField::MessageField(QWidget *parent, not_null<Window::Controller*> controller, const style::FlatTextarea &st, base::lambda<QString()> placeholderFactory, const QString &val) : Ui::FlatTextarea(parent, st, std::move(placeholderFactory), val)
, _controller(controller) {
setMinHeight(st::historySendSize.height() - 2 * st::historySendPadding);

View File

@@ -17,8 +17,13 @@ class Controller;
QString ConvertTagToMimeTag(const QString &tagId);
EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags);
TextWithTags::Tags ConvertEntitiesToTextTags(const EntitiesInText &entities);
std::unique_ptr<QMimeData> MimeDataFromTextWithEntities(const TextWithEntities &forClipboard);
TextWithTags::Tags ConvertEntitiesToTextTags(
const EntitiesInText &entities);
std::unique_ptr<QMimeData> MimeDataFromTextWithEntities(
const TextWithEntities &forClipboard);
void SetClipboardWithEntities(
const TextWithEntities &forClipboard,
QClipboard::Mode mode = QClipboard::Clipboard);
class MessageField final : public Ui::FlatTextarea {
Q_OBJECT

View File

@@ -8,12 +8,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "stickers.h"
#include "data/data_document.h"
#include "data/data_session.h"
#include "boxes/stickers_box.h"
#include "boxes/confirm_box.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "storage/localstorage.h"
#include "mainwidget.h"
#include "auth_session.h"
#include "mainwindow.h"
#include "ui/toast/toast.h"
#include "styles/style_chat_helpers.h"
@@ -70,22 +72,36 @@ void ApplyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) {
Ui::Toast::Show(toast);
// Ui::show(Box<StickersBox>(archived), LayerOption::KeepOther);
Auth().data().markStickersUpdated();
Auth().data().notifyStickersUpdated();
}
// For testing: Just apply random subset or your sticker sets as archived.
bool ApplyArchivedResultFake() {
auto sets = QVector<MTPStickerSetCovered>();
for (auto &set : Auth().data().stickerSetsRef()) {
if ((set.flags & MTPDstickerSet::Flag::f_installed) && !(set.flags & MTPDstickerSet_ClientFlag::f_special)) {
if ((set.flags & MTPDstickerSet::Flag::f_installed_date)
&& !(set.flags & MTPDstickerSet_ClientFlag::f_special)) {
if (rand_value<uint32>() % 128 < 64) {
auto data = MTP_stickerSet(MTP_flags(set.flags | MTPDstickerSet::Flag::f_archived), MTP_long(set.id), MTP_long(set.access), MTP_string(set.title), MTP_string(set.shortName), MTP_int(set.count), MTP_int(set.hash));
sets.push_back(MTP_stickerSetCovered(data, MTP_documentEmpty(MTP_long(0))));
auto data = MTP_stickerSet(
MTP_flags(set.flags | MTPDstickerSet::Flag::f_archived),
MTP_int(set.installDate),
MTP_long(set.id),
MTP_long(set.access),
MTP_string(set.title),
MTP_string(set.shortName),
MTP_int(set.count),
MTP_int(set.hash));
sets.push_back(MTP_stickerSetCovered(
data,
MTP_documentEmpty(MTP_long(0))));
}
}
}
if (sets.size() > 3) sets = sets.mid(0, 3);
auto fakeResult = MTP_messages_stickerSetInstallResultArchive(MTP_vector<MTPStickerSetCovered>(sets));
if (sets.size() > 3) {
sets = sets.mid(0, 3);
}
auto fakeResult = MTP_messages_stickerSetInstallResultArchive(
MTP_vector<MTPStickerSetCovered>(sets));
ApplyArchivedResult(fakeResult.c_messages_stickerSetInstallResultArchive());
return true;
}
@@ -99,7 +115,8 @@ void InstallLocally(uint64 setId) {
auto flags = it->flags;
it->flags &= ~(MTPDstickerSet::Flag::f_archived | MTPDstickerSet_ClientFlag::f_unread);
it->flags |= MTPDstickerSet::Flag::f_installed;
it->flags |= MTPDstickerSet::Flag::f_installed_date;
it->installDate = unixtime();
auto changedFlags = flags ^ it->flags;
auto &order = Auth().data().stickerSetsOrderRef();
@@ -122,7 +139,9 @@ void InstallLocally(uint64 setId) {
}
}
Local::writeInstalledStickers();
if (changedFlags & MTPDstickerSet_ClientFlag::f_unread) Local::writeFeaturedStickers();
if (changedFlags & MTPDstickerSet_ClientFlag::f_unread) {
Local::writeFeaturedStickers();
}
if (changedFlags & MTPDstickerSet::Flag::f_archived) {
auto index = Auth().data().archivedStickerSetsOrderRef().indexOf(setId);
if (index >= 0) {
@@ -130,7 +149,7 @@ void InstallLocally(uint64 setId) {
Local::writeArchivedStickers();
}
}
Auth().data().markStickersUpdated();
Auth().data().notifyStickersUpdated();
}
void UndoInstallLocally(uint64 setId) {
@@ -140,7 +159,8 @@ void UndoInstallLocally(uint64 setId) {
return;
}
it->flags &= ~MTPDstickerSet::Flag::f_installed;
it->flags &= ~MTPDstickerSet::Flag::f_installed_date;
it->installDate = TimeId(0);
auto &order = Auth().data().stickerSetsOrderRef();
int currentIndex = order.indexOf(setId);
@@ -149,7 +169,7 @@ void UndoInstallLocally(uint64 setId) {
}
Local::writeInstalledStickers();
Auth().data().markStickersUpdated();
Auth().data().notifyStickersUpdated();
Ui::show(
Box<InformBox>(lang(lng_stickers_not_found)),
@@ -215,7 +235,15 @@ void SetIsFaved(not_null<DocumentData*> document, base::optional<std::vector<not
auto &sets = Auth().data().stickerSetsRef();
auto it = sets.find(FavedSetId);
if (it == sets.end()) {
it = sets.insert(FavedSetId, Set(FavedSetId, 0, Lang::Hard::FavedSetTitle(), QString(), 0, 0, MTPDstickerSet_ClientFlag::f_special | 0));
it = sets.insert(FavedSetId, Set(
FavedSetId,
uint64(0),
Lang::Hard::FavedSetTitle(),
QString(),
0, // count
0, // hash
MTPDstickerSet_ClientFlag::f_special | 0,
TimeId(0)));
}
auto index = it->stickers.indexOf(document);
if (index == 0) {
@@ -232,7 +260,7 @@ void SetIsFaved(not_null<DocumentData*> document, base::optional<std::vector<not
return;
}
Local::writeFavedStickers();
Auth().data().markStickersUpdated();
Auth().data().notifyStickersUpdated();
Auth().api().stickerSetInstalled(FavedSetId);
}
@@ -300,7 +328,7 @@ void SetIsNotFaved(not_null<DocumentData*> document) {
sets.erase(it);
}
Local::writeFavedStickers();
Auth().data().markStickersUpdated();
Auth().data().notifyStickersUpdated();
}
void SetFaved(not_null<DocumentData*> document, bool faved) {
@@ -319,7 +347,9 @@ void SetsReceived(const QVector<MTPStickerSet> &data, int32 hash) {
QMap<uint64, uint64> setsToRequest;
for (auto &set : sets) {
if (!(set.flags & MTPDstickerSet::Flag::f_archived)) {
set.flags &= ~MTPDstickerSet::Flag::f_installed; // mark for removing
// Mark for removing.
set.flags &= ~MTPDstickerSet::Flag::f_installed_date;
set.installDate = 0;
}
}
for_const (auto &setData, data) {
@@ -336,7 +366,7 @@ void SetsReceived(const QVector<MTPStickerSet> &data, int32 hash) {
auto writeRecent = false;
auto &recent = GetRecentPack();
for (auto it = sets.begin(), e = sets.end(); it != e;) {
bool installed = (it->flags & MTPDstickerSet::Flag::f_installed);
bool installed = (it->flags & MTPDstickerSet::Flag::f_installed_date);
bool featured = (it->flags & MTPDstickerSet_ClientFlag::f_featured);
bool special = (it->flags & MTPDstickerSet_ClientFlag::f_special);
bool archived = (it->flags & MTPDstickerSet::Flag::f_archived);
@@ -372,11 +402,16 @@ void SetsReceived(const QVector<MTPStickerSet> &data, int32 hash) {
LOG(("API Error: received stickers hash %1 while counted hash is %2").arg(hash).arg(Local::countStickersHash()));
}
Auth().data().markStickersUpdated();
Auth().data().notifyStickersUpdated();
}
void SetPackAndEmoji(Set &set, Pack &&pack, const QVector<MTPStickerPack> &packs) {
void SetPackAndEmoji(
Set &set,
Pack &&pack,
const std::vector<TimeId> &&dates,
const QVector<MTPStickerPack> &packs) {
set.stickers = std::move(pack);
set.dates = std::move(dates);
set.emoji.clear();
for_const (auto &mtpPack, packs) {
Assert(mtpPack.type() == mtpc_stickerPack);
@@ -388,7 +423,7 @@ void SetPackAndEmoji(Set &set, Pack &&pack, const QVector<MTPStickerPack> &packs
auto p = Pack();
p.reserve(stickers.size());
for (auto j = 0, c = stickers.size(); j != c; ++j) {
auto document = App::document(stickers[j].v);
auto document = Auth().data().document(stickers[j].v);
if (!document || !document->sticker()) continue;
p.push_back(document);
@@ -398,31 +433,52 @@ void SetPackAndEmoji(Set &set, Pack &&pack, const QVector<MTPStickerPack> &packs
}
}
void SpecialSetReceived(uint64 setId, const QString &setTitle, const QVector<MTPDocument> &items, int32 hash, const QVector<MTPStickerPack> &packs) {
void SpecialSetReceived(
uint64 setId,
const QString &setTitle,
const QVector<MTPDocument> &items,
int32 hash,
const QVector<MTPStickerPack> &packs,
const QVector<MTPint> &usageDates) {
auto &sets = Auth().data().stickerSetsRef();
auto it = sets.find(setId);
auto &d_docs = items;
if (d_docs.isEmpty()) {
if (items.isEmpty()) {
if (it != sets.cend()) {
sets.erase(it);
}
} else {
if (it == sets.cend()) {
it = sets.insert(setId, Set(setId, 0, setTitle, QString(), 0, 0, MTPDstickerSet_ClientFlag::f_special | 0));
it = sets.insert(setId, Set(
setId,
uint64(0),
setTitle,
QString(),
0, // count
0, // hash
MTPDstickerSet_ClientFlag::f_special | 0,
TimeId(0)));
} else {
it->title = setTitle;
}
it->hash = hash;
auto dates = std::vector<TimeId>();
auto dateIndex = 0;
auto datesAvailable = (items.size() == usageDates.size());
auto custom = sets.find(CustomSetId);
auto pack = Pack();
pack.reserve(d_docs.size());
for_const (auto &mtpDocument, d_docs) {
auto document = App::feedDocument(mtpDocument);
if (!document || !document->sticker()) continue;
pack.reserve(items.size());
for_const (auto &mtpDocument, items) {
const auto date = datesAvailable
? TimeId(usageDates[dateIndex++].v)
: TimeId();
auto document = Auth().data().document(mtpDocument);
if (!document->sticker()) continue;
pack.push_back(document);
dates.push_back(date);
if (custom != sets.cend()) {
auto index = custom->stickers.indexOf(document);
if (index >= 0) {
@@ -449,7 +505,7 @@ void SpecialSetReceived(uint64 setId, const QString &setTitle, const QVector<MTP
if (pack.isEmpty()) {
sets.erase(it);
} else {
SetPackAndEmoji(*it, std::move(pack), packs);
SetPackAndEmoji(*it, std::move(pack), std::move(dates), packs);
}
if (writeRecent) {
@@ -473,7 +529,7 @@ void SpecialSetReceived(uint64 setId, const QString &setTitle, const QVector<MTP
default: Unexpected("setId in SpecialSetReceived()");
}
Auth().data().markStickersUpdated();
Auth().data().notifyStickersUpdated();
}
void FeaturedSetsReceived(const QVector<MTPStickerSetCovered> &data, const QVector<MTPlong> &unread, int32 hash) {
@@ -510,13 +566,25 @@ void FeaturedSetsReceived(const QVector<MTPStickerSetCovered> &data, const QVect
if (set) {
auto it = sets.find(set->vid.v);
auto title = GetSetTitle(*set);
const auto title = GetSetTitle(*set);
const auto installDate = set->has_installed_date()
? set->vinstalled_date.v
: TimeId(0);
if (it == sets.cend()) {
auto setClientFlags = MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_not_loaded;
auto setClientFlags = MTPDstickerSet_ClientFlag::f_featured
| MTPDstickerSet_ClientFlag::f_not_loaded;
if (unreadMap.contains(set->vid.v)) {
setClientFlags |= MTPDstickerSet_ClientFlag::f_unread;
}
it = sets.insert(set->vid.v, Set(set->vid.v, set->vaccess_hash.v, title, qs(set->vshort_name), set->vcount.v, set->vhash.v, set->vflags.v | setClientFlags));
it = sets.insert(set->vid.v, Set(
set->vid.v,
set->vaccess_hash.v,
title,
qs(set->vshort_name),
set->vcount.v,
set->vhash.v,
set->vflags.v | setClientFlags,
installDate));
} else {
it->access = set->vaccess_hash.v;
it->title = title;
@@ -524,6 +592,7 @@ void FeaturedSetsReceived(const QVector<MTPStickerSetCovered> &data, const QVect
auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_not_loaded | MTPDstickerSet_ClientFlag::f_special);
it->flags = set->vflags.v | clientFlags;
it->flags |= MTPDstickerSet_ClientFlag::f_featured;
it->installDate = installDate;
if (unreadMap.contains(it->id)) {
it->flags |= MTPDstickerSet_ClientFlag::f_unread;
} else {
@@ -544,7 +613,7 @@ void FeaturedSetsReceived(const QVector<MTPStickerSetCovered> &data, const QVect
auto unreadCount = 0;
for (auto it = sets.begin(), e = sets.end(); it != e;) {
bool installed = (it->flags & MTPDstickerSet::Flag::f_installed);
bool installed = (it->flags & MTPDstickerSet::Flag::f_installed_date);
bool featured = (it->flags & MTPDstickerSet_ClientFlag::f_featured);
bool special = (it->flags & MTPDstickerSet_ClientFlag::f_special);
bool archived = (it->flags & MTPDstickerSet::Flag::f_archived);
@@ -573,7 +642,7 @@ void FeaturedSetsReceived(const QVector<MTPStickerSetCovered> &data, const QVect
Local::writeFeaturedStickers();
Auth().data().markStickersUpdated();
Auth().data().notifyStickersUpdated();
}
void GifsReceived(const QVector<MTPDocument> &items, int32 hash) {
@@ -582,8 +651,8 @@ void GifsReceived(const QVector<MTPDocument> &items, int32 hash) {
saved.reserve(items.size());
for_const (auto &gif, items) {
auto document = App::feedDocument(gif);
if (!document || !document->isGifv()) {
auto document = Auth().data().document(gif);
if (!document->isGifv()) {
LOG(("API Error: bad document returned in HistoryWidget::savedGifsGot!"));
continue;
}
@@ -596,51 +665,151 @@ void GifsReceived(const QVector<MTPDocument> &items, int32 hash) {
Local::writeSavedGifs();
Auth().data().markSavedGifsUpdated();
Auth().data().notifySavedGifsUpdated();
}
Pack GetListByEmoji(not_null<EmojiPtr> emoji) {
auto original = emoji->original();
auto result = Pack();
auto setsToRequest = QMap<uint64, uint64>();
auto &sets = Auth().data().stickerSetsRef();
std::vector<not_null<DocumentData*>> GetListByEmoji(
not_null<EmojiPtr> emoji,
uint64 seed) {
const auto original = emoji->original();
auto faved = Pack();
auto favedIt = sets.find(Stickers::FavedSetId);
if (favedIt != sets.cend()) {
auto i = favedIt->emoji.constFind(original);
if (i != favedIt->emoji.cend()) {
faved = *i;
result = faved;
struct StickerWithDate {
not_null<DocumentData*> document;
TimeId date = 0;
};
auto result = std::vector<StickerWithDate>();
auto &sets = Auth().data().stickerSetsRef();
auto setsToRequest = base::flat_map<uint64, uint64>();
const auto add = [&](not_null<DocumentData*> document, TimeId date) {
if (ranges::find(result, document, [](const StickerWithDate &data) {
return data.document;
}) == result.end()) {
result.push_back({ document, date });
}
}
auto &order = Auth().data().stickerSetsOrder();
for (auto i = 0, l = order.size(); i != l; ++i) {
auto it = sets.find(order[i]);
if (it != sets.cend()) {
if (it->emoji.isEmpty()) {
setsToRequest.insert(it->id, it->access);
it->flags |= MTPDstickerSet_ClientFlag::f_not_loaded;
} else if (!(it->flags & MTPDstickerSet::Flag::f_archived)) {
auto i = it->emoji.constFind(original);
if (i != it->emoji.cend()) {
result.reserve(result.size() + i->size());
for_const (auto sticker, *i) {
if (!faved.contains(sticker)) {
result.push_back(sticker);
}
};
constexpr auto kSlice = 65536;
const auto CreateSortKey = [&](
not_null<DocumentData*> document,
int base) {
return TimeId(base + int((document->id ^ seed) % kSlice));
};
const auto CreateRecentSortKey = [&](not_null<DocumentData*> document) {
return CreateSortKey(document, kSlice * 4);
};
auto myCounter = 0;
const auto CreateMySortKey = [&] {
return (kSlice * 4 - (++myCounter));
};
const auto CreateFeaturedSortKey = [&](not_null<DocumentData*> document) {
return CreateSortKey(document, kSlice * 2);
};
const auto CreateOtherSortKey = [&](not_null<DocumentData*> document) {
return CreateSortKey(document, kSlice);
};
const auto InstallDate = [&](not_null<DocumentData*> document) {
Expects(document->sticker() != nullptr);
const auto sticker = document->sticker();
if (sticker->set.type() == mtpc_inputStickerSetID) {
const auto setId = sticker->set.c_inputStickerSetID().vid.v;
const auto setIt = sets.find(setId);
if (setIt != sets.end()) {
return setIt->installDate;
}
}
return TimeId(0);
};
auto recentIt = sets.find(Stickers::CloudRecentSetId);
if (recentIt != sets.cend()) {
auto i = recentIt->emoji.constFind(original);
if (i != recentIt->emoji.cend()) {
result.reserve(i->size());
for (const auto document : *i) {
const auto usageDate = [&] {
if (recentIt->dates.empty()) {
return TimeId(0);
}
}
const auto index = recentIt->stickers.indexOf(document);
if (index < 0) {
return TimeId(0);
}
Assert(index < recentIt->dates.size());
return recentIt->dates[index];
}();
const auto date = usageDate
? usageDate
: InstallDate(document);
result.push_back({
document,
date ? date : CreateRecentSortKey(document) });
}
}
}
if (!setsToRequest.isEmpty()) {
for (auto i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) {
Auth().api().scheduleStickerSetRequest(i.key(), i.value());
const auto addList = [&](const Order &order, MTPDstickerSet::Flag skip) {
for (const auto setId : order) {
auto it = sets.find(setId);
if (it == sets.cend() || (it->flags & skip)) {
continue;
}
if (it->emoji.isEmpty()) {
setsToRequest.emplace(it->id, it->access);
it->flags |= MTPDstickerSet_ClientFlag::f_not_loaded;
continue;
}
auto i = it->emoji.constFind(original);
if (i == it->emoji.cend()) {
continue;
}
const auto my = (it->flags & MTPDstickerSet::Flag::f_installed_date);
result.reserve(result.size() + i->size());
for (const auto document : *i) {
const auto installDate = my ? it->installDate : TimeId(0);
const auto date = (installDate > 1)
? installDate
: my
? CreateMySortKey()
: CreateFeaturedSortKey(document);
add(document, date);
}
}
};
addList(
Auth().data().stickerSetsOrder(),
MTPDstickerSet::Flag::f_archived);
addList(
Auth().data().featuredStickerSetsOrder(),
MTPDstickerSet::Flag::f_installed_date);
if (!setsToRequest.empty()) {
for (const auto [setId, accessHash] : setsToRequest) {
Auth().api().scheduleStickerSetRequest(setId, accessHash);
}
Auth().api().requestStickerSets();
}
return result;
const auto others = Auth().api().stickersByEmoji(original);
if (!others) {
return {};
}
result.reserve(result.size() + others->size());
for (const auto document : *others) {
add(document, CreateOtherSortKey(document));
}
ranges::action::sort(
result,
std::greater<>(),
[](const StickerWithDate &data) { return data.date; });
return ranges::view::all(
result
) | ranges::view::transform([](const StickerWithDate &data) {
return data.document;
}) | ranges::to_vector;
}
base::optional<std::vector<not_null<EmojiPtr>>> GetEmojiListFromSet(
@@ -675,15 +844,32 @@ Set *FeedSet(const MTPDstickerSet &set) {
auto title = GetSetTitle(set);
auto flags = MTPDstickerSet::Flags(0);
if (it == sets.cend()) {
it = sets.insert(set.vid.v, Stickers::Set(set.vid.v, set.vaccess_hash.v, title, qs(set.vshort_name), set.vcount.v, set.vhash.v, set.vflags.v | MTPDstickerSet_ClientFlag::f_not_loaded));
it = sets.insert(set.vid.v, Stickers::Set(
set.vid.v,
set.vaccess_hash.v,
title,
qs(set.vshort_name),
set.vcount.v,
set.vhash.v,
set.vflags.v | MTPDstickerSet_ClientFlag::f_not_loaded,
set.has_installed_date() ? set.vinstalled_date.v : TimeId(0)));
} else {
it->access = set.vaccess_hash.v;
it->title = title;
it->shortName = qs(set.vshort_name);
flags = it->flags;
auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_not_loaded | MTPDstickerSet_ClientFlag::f_special);
auto clientFlags = it->flags
& (MTPDstickerSet_ClientFlag::f_featured
| MTPDstickerSet_ClientFlag::f_unread
| MTPDstickerSet_ClientFlag::f_not_loaded
| MTPDstickerSet_ClientFlag::f_special);
it->flags = set.vflags.v | clientFlags;
if (it->count != set.vcount.v || it->hash != set.vhash.v || it->emoji.isEmpty()) {
it->installDate = set.has_installed_date()
? set.vinstalled_date.v
: TimeId(0);
if (it->count != set.vcount.v
|| it->hash != set.vhash.v
|| it->emoji.isEmpty()) {
it->count = set.vcount.v;
it->hash = set.vhash.v;
it->flags |= MTPDstickerSet_ClientFlag::f_not_loaded; // need to request this set
@@ -718,8 +904,8 @@ Set *FeedSetFull(const MTPmessages_StickerSet &data) {
auto pack = Pack();
pack.reserve(d_docs.size());
for (auto i = 0, l = d_docs.size(); i != l; ++i) {
auto doc = App::feedDocument(d_docs.at(i));
if (!doc || !doc->sticker()) continue;
auto doc = Auth().data().document(d_docs.at(i));
if (!doc->sticker()) continue;
pack.push_back(doc);
if (custom != sets.cend()) {
@@ -765,7 +951,7 @@ Set *FeedSetFull(const MTPmessages_StickerSet &data) {
Pack p;
p.reserve(stickers.size());
for (auto j = 0, c = stickers.size(); j != c; ++j) {
auto doc = App::document(stickers[j].v);
auto doc = Auth().data().document(stickers[j].v);
if (!doc || !doc->sticker()) continue;
p.push_back(doc);
@@ -780,7 +966,7 @@ Set *FeedSetFull(const MTPmessages_StickerSet &data) {
}
if (set) {
if (set->flags & MTPDstickerSet::Flag::f_installed) {
if (set->flags & MTPDstickerSet::Flag::f_installed_date) {
if (!(set->flags & MTPDstickerSet::Flag::f_archived)) {
Local::writeInstalledStickers();
}
@@ -790,7 +976,7 @@ Set *FeedSetFull(const MTPmessages_StickerSet &data) {
}
}
Auth().data().markStickersUpdated();
Auth().data().notifyStickersUpdated();
return set;
}
@@ -811,7 +997,7 @@ RecentStickerPack &GetRecentPack() {
auto &recent = cRefRecentStickers();
recent.reserve(p.size());
for (const auto &preloaded : p) {
const auto document = App::document(preloaded.first);
const auto document = Auth().data().document(preloaded.first);
if (!document || !document->sticker()) continue;
recent.push_back(qMakePair(document, preloaded.second));

View File

@@ -26,20 +26,34 @@ using Pack = QVector<DocumentData*>;
using ByEmojiMap = QMap<EmojiPtr, Pack>;
struct Set {
Set(uint64 id, uint64 access, const QString &title, const QString &shortName, int32 count, int32 hash, MTPDstickerSet::Flags flags)
: id(id)
, access(access)
, title(title)
, shortName(shortName)
, count(count)
, hash(hash)
, flags(flags) {
Set(
uint64 id,
uint64 access,
const QString &title,
const QString &shortName,
int count,
int32 hash,
MTPDstickerSet::Flags flags,
TimeId installDate)
: id(id)
, access(access)
, title(title)
, shortName(shortName)
, count(count)
, hash(hash)
, flags(flags)
, installDate(installDate) {
}
uint64 id, access;
uint64 id = 0;
uint64 access = 0;
QString title, shortName;
int32 count, hash;
int count = 0;
int32 hash = 0;
MTPDstickerSet::Flags flags;
TimeId installDate = 0;
Pack stickers;
std::vector<TimeId> dates;
Pack covers;
ByEmojiMap emoji;
};
using Sets = QMap<uint64, Set>;
@@ -59,11 +73,22 @@ bool IsFaved(not_null<DocumentData*> document);
void SetFaved(not_null<DocumentData*> document, bool faved);
void SetsReceived(const QVector<MTPStickerSet> &data, int32 hash);
void SpecialSetReceived(uint64 setId, const QString &setTitle, const QVector<MTPDocument> &items, int32 hash, const QVector<MTPStickerPack> &packs = QVector<MTPStickerPack>());
void FeaturedSetsReceived(const QVector<MTPStickerSetCovered> &data, const QVector<MTPlong> &unread, int32 hash);
void SpecialSetReceived(
uint64 setId,
const QString &setTitle,
const QVector<MTPDocument> &items,
int32 hash,
const QVector<MTPStickerPack> &packs = QVector<MTPStickerPack>(),
const QVector<MTPint> &usageDates = QVector<MTPint>());
void FeaturedSetsReceived(
const QVector<MTPStickerSetCovered> &data,
const QVector<MTPlong> &unread,
int32 hash);
void GifsReceived(const QVector<MTPDocument> &items, int32 hash);
Pack GetListByEmoji(not_null<EmojiPtr> emoji);
std::vector<not_null<DocumentData*>> GetListByEmoji(
not_null<EmojiPtr> emoji,
uint64 seed);
base::optional<std::vector<not_null<EmojiPtr>>> GetEmojiListFromSet(
not_null<DocumentData*> document);

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "chat_helpers/tabbed_selector.h"
#include "chat_helpers/stickers.h"
#include "base/variant.h"
#include "base/timer.h"
namespace Window {
class Controller;
@@ -16,6 +18,7 @@ class Controller;
namespace Ui {
class LinkButton;
class RippleAnimation;
} // namespace Ui
namespace ChatHelpers {
@@ -29,7 +32,9 @@ class StickersListWidget
Q_OBJECT
public:
StickersListWidget(QWidget *parent, not_null<Window::Controller*> controller);
StickersListWidget(
QWidget *parent,
not_null<Window::Controller*> controller);
void refreshRecent() override;
void preloadImages() override;
@@ -39,6 +44,9 @@ public:
void showStickerSet(uint64 setId);
void showMegagroupSet(ChannelData *megagroup);
void afterShown() override;
void beforeHiding() override;
void refreshStickers();
void fillIcons(QList<StickerIcon> &icons);
@@ -50,6 +58,9 @@ public:
void notInstalledLocally(uint64 setId);
void clearInstalledLocally();
void sendSearchRequest();
void searchForSets(const QString &query);
~StickersListWidget();
protected:
@@ -86,6 +97,7 @@ private:
enum class Section {
Featured,
Stickers,
Search,
};
struct OverSticker {
@@ -129,17 +141,18 @@ private:
uint64 id,
MTPDstickerSet::Flags flags,
const QString &title,
int hoversSize,
bool externalLayout,
const Stickers::Pack &pack = Stickers::Pack());
Set(Set &&other);
Set &operator=(Set &&other);
~Set();
uint64 id;
MTPDstickerSet::Flags flags;
uint64 id = 0;
MTPDstickerSet::Flags flags = MTPDstickerSet::Flags();
QString title;
Stickers::Pack pack;
std::unique_ptr<Ui::RippleAnimation> ripple;
bool externalLayout = false;
};
template <typename Callback>
@@ -151,9 +164,15 @@ private:
void installSet(uint64 setId);
void removeMegagroupSet(bool locally);
void removeSet(uint64 setId);
void sendInstallRequest(
uint64 setId,
const MTPInputStickerSet &input);
void refreshSearchSets();
void refreshSearchIndex();
bool setHasTitle(const Set &set) const;
bool stickerHasDeleteButton(const Set &set, int index) const;
Stickers::Pack collectRecentStickers();
void refreshRecentStickers(bool resize = true);
void refreshFavedStickers();
enum class GroupStickersPlace {
@@ -176,12 +195,8 @@ private:
};
void validateSelectedIcon(ValidateIconAnimations animations);
std::vector<Set> &shownSets() {
return (_section == Section::Featured) ? _featuredSets : _mySets;
}
const std::vector<Set> &shownSets() const {
return (_section == Section::Featured) ? _featuredSets : _mySets;
}
std::vector<Set> &shownSets();
const std::vector<Set> &shownSets() const;
int featuredRowHeight() const;
void readVisibleSets();
@@ -189,6 +204,7 @@ private:
void paintStickers(Painter &p, QRect clip);
void paintMegagroupEmptySet(Painter &p, int y, bool buttonSelected, TimeMs ms);
void paintSticker(Painter &p, Set &set, int y, int index, bool selected, bool deleteSelected);
void paintEmptySearchResults(Painter &p);
int stickersRight() const;
bool featuredHasAddButton(int index) const;
@@ -207,6 +223,7 @@ private:
void appendSet(
std::vector<Set> &to,
uint64 setId,
bool externalLayout,
AppendSkip skip = AppendSkip::None);
void selectEmoji(EmojiPtr emoji);
@@ -218,9 +235,19 @@ private:
void setColumnCount(int count);
void refreshFooterIcons();
void cancelSetsSearch();
void showSearchResults();
void searchResultsDone(const MTPmessages_FoundStickerSets &result);
void refreshSearchRows();
void refreshSearchRows(const std::vector<Stickers::Set*> *cloudSets);
void fillLocalSearchRows(const QString &query);
void fillCloudSearchRows(const std::vector<Stickers::Set*> &sets);
void addSearchRow(not_null<const Stickers::Set*> set);
ChannelData *_megagroupSet = nullptr;
std::vector<Set> _mySets;
std::vector<Set> _featuredSets;
std::vector<Set> _searchSets;
base::flat_set<uint64> _installedLocallySets;
std::vector<bool> _custom;
base::flat_set<not_null<DocumentData*>> _favedStickersMap;
@@ -253,6 +280,12 @@ private:
QTimer _previewTimer;
bool _previewShown = false;
std::map<QString, std::vector<Stickers::Set*>> _searchCache;
std::vector<std::pair<uint64, QStringList>> _searchIndex;
base::Timer _searchRequestTimer;
QString _searchQuery, _searchNextQuery;
mtpRequestId _searchRequestId = 0;
};
} // namespace ChatHelpers

View File

@@ -277,7 +277,7 @@ TabbedSelector::TabbedSelector(QWidget *parent, not_null<Window::Controller*> co
Tab { SelectorTab::Stickers, object_ptr<StickersListWidget>(this, controller) },
Tab { SelectorTab::Gifs, object_ptr<GifsListWidget>(this, controller) },
} }
, _currentTabType(Auth().data().selectorTab()) {
, _currentTabType(Auth().settings().selectorTab()) {
resize(st::emojiPanWidth, st::emojiPanMaxHeight);
for (auto &tab : _tabs) {
@@ -685,8 +685,8 @@ void TabbedSelector::switchTab() {
_a_slide.start([this] { update(); }, 0., 1., st::emojiPanSlideDuration, anim::linear);
update();
Auth().data().setSelectorTab(_currentTabType);
Auth().saveDataDelayed();
Auth().settings().setSelectorTab(_currentTabType);
Auth().saveSettingsDelayed();
}
not_null<EmojiListWidget*> TabbedSelector::emoji() const {

View File

@@ -90,8 +90,6 @@ enum {
PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request
SearchPeopleLimit = 5,
MinUsernameLength = 5,
MaxUsernameLength = 32,
UsernameCheckTimeout = 200,
MaxPhotoCaption = 200,

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h"
#include "lang/lang_keys.h"
#include "data/data_session.h"
#include "mainwindow.h"
#include "apiwrap.h"
@@ -71,6 +72,15 @@ std::map<int, const char*> AlphaLogs() {
"\xE2\x80\x94 Select a message you want to reply to by "
"pressing Ctrl+Up and Ctrl+Down."
},
{
1002009,
"\xE2\x80\x94 Quick Reply. "
"Double click near a message for a quick reply.\n"
"\xE2\x80\x94 Search for Stickers. "
"Click on the new search icon to access "
"your sticker sets or find new ones."
}
};
}

View File

@@ -48,7 +48,8 @@ public:
}
// Copy to clipboard support.
virtual void copyToClipboard() const {
virtual QString copyToClipboardText() const {
return QString();
}
virtual QString copyToClipboardContextItemText() const {
return QString();

View File

@@ -10,6 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "messenger.h"
#include "platform/platform_specific.h"
#include "history/view/history_view_element.h"
#include "history/history_item.h"
#include "boxes/confirm_box.h"
#include "base/qthelp_regex.h"
#include "base/qthelp_url.h"
@@ -36,12 +38,16 @@ QString tryConvertUrlToLocal(QString url) {
} else if (auto confirmPhoneMatch = regex_match(qsl("^confirmphone/?\\?(.+)"), query, matchOptions)) {
return qsl("tg://confirmphone?") + confirmPhoneMatch->captured(1);
} else if (auto ivMatch = regex_match(qsl("iv/?\\?(.+)(#|$)"), query, matchOptions)) {
auto params = url_parse_params(ivMatch->captured(1), UrlParamNameTransform::ToLower);
auto previewedUrl = params.value(qsl("url"));
if (previewedUrl.startsWith(qstr("http://"), Qt::CaseInsensitive)
|| previewedUrl.startsWith(qstr("https://"), Qt::CaseInsensitive)) {
return previewedUrl;
}
//
// We need to show our t.me page, not the url directly.
//
//auto params = url_parse_params(ivMatch->captured(1), UrlParamNameTransform::ToLower);
//auto previewedUrl = params.value(qsl("url"));
//if (previewedUrl.startsWith(qstr("http://"), Qt::CaseInsensitive)
// || previewedUrl.startsWith(qstr("https://"), Qt::CaseInsensitive)) {
// return previewedUrl;
//}
return url;
} else if (auto socksMatch = regex_match(qsl("socks/?\\?(.+)(#|$)"), query, matchOptions)) {
return qsl("tg://socks?") + socksMatch->captured(1);
} else if (auto usernameMatch = regex_match(qsl("^([a-zA-Z0-9\\.\\_]+)(/?\\?|/?$|/(\\d+)/?(?:\\?|$))"), query, matchOptions)) {
@@ -63,6 +69,20 @@ bool UrlRequiresConfirmation(const QUrl &url) {
} // namespace
UrlClickHandler::UrlClickHandler(const QString &url, bool fullDisplayed)
: TextClickHandler(fullDisplayed)
, _originalUrl(url) {
if (isEmail()) {
_readable = _originalUrl;
} else {
const auto original = QUrl(_originalUrl);
const auto good = QUrl(original.isValid()
? original.toEncoded()
: QString());
_readable = good.isValid() ? good.toDisplayString() : _originalUrl;
}
}
QString UrlClickHandler::copyToClipboardContextItemText() const {
return lang(isEmail() ? lng_context_copy_email : lng_context_copy_link);
}
@@ -228,6 +248,23 @@ TextWithEntities HashtagClickHandler::getExpandedLinkTextWithEntities(ExpandLink
return simpleTextWithEntity({ EntityInTextHashtag, entityOffset, textPart.size() });
}
QString CashtagClickHandler::copyToClipboardContextItemText() const {
return lang(lng_context_copy_hashtag);
}
void CashtagClickHandler::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
App::searchByHashtag(_tag, Ui::getPeerForMouseAction());
}
}
TextWithEntities CashtagClickHandler::getExpandedLinkTextWithEntities(
ExpandLinksMode mode,
int entityOffset,
const QStringRef &textPart) const {
return simpleTextWithEntity({ EntityInTextCashtag, entityOffset, textPart.size() });
}
PeerData *BotCommandClickHandler::_peer = nullptr;
UserData *BotCommandClickHandler::_bot = nullptr;
void BotCommandClickHandler::onClick(Qt::MouseButton button) const {
@@ -241,10 +278,11 @@ void BotCommandClickHandler::onClick(Qt::MouseButton button) const {
}
if (auto peer = Ui::getPeerForMouseAction()) { // old way
UserData *bot = peer->isUser() ? peer->asUser() : nullptr;
if (auto item = App::hoveredLinkItem()) {
if (!bot) {
bot = item->fromOriginal()->asUser(); // may return nullptr
auto bot = peer->isUser() ? peer->asUser() : nullptr;
if (!bot) {
if (const auto view = App::hoveredLinkItem()) {
// may return nullptr
bot = view->data()->fromOriginal()->asUser();
}
}
Ui::showPeerHistory(peer, ShowAtTheEndMsgId);

View File

@@ -12,14 +12,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class TextClickHandler : public ClickHandler {
public:
TextClickHandler(bool fullDisplayed = true) : _fullDisplayed(fullDisplayed) {
TextClickHandler(bool fullDisplayed = true)
: _fullDisplayed(fullDisplayed) {
}
void copyToClipboard() const override {
auto u = url();
if (!u.isEmpty()) {
QApplication::clipboard()->setText(u);
}
QString copyToClipboardText() const override {
return url();
}
QString tooltip() const override {
@@ -42,22 +40,21 @@ protected:
class UrlClickHandler : public TextClickHandler {
public:
UrlClickHandler(const QString &url, bool fullDisplayed = true) : TextClickHandler(fullDisplayed), _originalUrl(url) {
if (isEmail()) {
_readable = _originalUrl;
} else {
QUrl u(_originalUrl), good(u.isValid() ? u.toEncoded() : QString());
_readable = good.isValid() ? good.toDisplayString() : _originalUrl;
}
}
UrlClickHandler(const QString &url, bool fullDisplayed = true);
QString copyToClipboardContextItemText() const override;
QString dragText() const override {
return url();
}
QString getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const override;
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
QString getExpandedLinkText(
ExpandLinksMode mode,
const QStringRef &textPart) const override;
TextWithEntities getExpandedLinkTextWithEntities(
ExpandLinksMode mode,
int entityOffset,
const QStringRef &textPart) const override;
static void doOpen(QString url);
void onClick(Qt::MouseButton button) const override {
@@ -102,14 +99,21 @@ public:
}
}
QString getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const override;
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
QString getExpandedLinkText(
ExpandLinksMode mode,
const QStringRef &textPart) const override;
TextWithEntities getExpandedLinkTextWithEntities(
ExpandLinksMode mode,
int entityOffset,
const QStringRef &textPart) const override;
};
class BotGameUrlClickHandler : public UrlClickHandler {
public:
BotGameUrlClickHandler(UserData *bot, QString url) : UrlClickHandler(url, false), _bot(bot) {
BotGameUrlClickHandler(UserData *bot, QString url)
: UrlClickHandler(url, false)
, _bot(bot) {
}
void onClick(Qt::MouseButton button) const override;
@@ -131,7 +135,10 @@ public:
QString copyToClipboardContextItemText() const override;
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
TextWithEntities getExpandedLinkTextWithEntities(
ExpandLinksMode mode,
int entityOffset,
const QStringRef &textPart) const override;
protected:
QString url() const override {
@@ -153,7 +160,10 @@ public:
void onClick(Qt::MouseButton button) const override;
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
TextWithEntities getExpandedLinkTextWithEntities(
ExpandLinksMode mode,
int entityOffset,
const QStringRef &textPart) const override;
QString tooltip() const override;
@@ -177,7 +187,38 @@ public:
QString copyToClipboardContextItemText() const override;
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
TextWithEntities getExpandedLinkTextWithEntities(
ExpandLinksMode mode,
int entityOffset,
const QStringRef &textPart) const override;
protected:
QString url() const override {
return _tag;
}
private:
QString _tag;
};
class CashtagClickHandler : public TextClickHandler {
public:
CashtagClickHandler(const QString &tag) : _tag(tag) {
}
void onClick(Qt::MouseButton button) const override;
QString dragText() const override {
return _tag;
}
QString copyToClipboardContextItemText() const override;
TextWithEntities getExpandedLinkTextWithEntities(
ExpandLinksMode mode,
int entityOffset,
const QStringRef &textPart) const override;
protected:
QString url() const override {
@@ -209,7 +250,10 @@ public:
_bot = bot;
}
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
TextWithEntities getExpandedLinkTextWithEntities(
ExpandLinksMode mode,
int entityOffset,
const QStringRef &textPart) const override;
protected:
QString url() const override {

View File

@@ -493,6 +493,31 @@ void SetAnnotation(const std::string &key, const QString &value) {
}
}
void SetAnnotationHex(const std::string &key, const QString &value) {
if (value.isEmpty()) {
return SetAnnotation(key, value);
}
const auto utf = value.toUtf8();
auto buffer = std::string();
buffer.reserve(4 * utf.size());
const auto hexDigit = [](std::uint8_t value) {
if (value >= 10) {
return 'A' + (value - 10);
}
return '0' + value;
};
const auto appendHex = [&](std::uint8_t value) {
buffer.push_back('\\');
buffer.push_back('x');
buffer.push_back(hexDigit(value / 16));
buffer.push_back(hexDigit(value % 16));
};
for (const auto ch : utf) {
appendHex(ch);
}
ProcessAnnotations[key] = std::move(buffer);
}
void SetAnnotationRef(const std::string &key, const QString *valuePtr) {
if (valuePtr) {
ProcessAnnotationRefs[key] = valuePtr;

View File

@@ -34,6 +34,7 @@ Status Restart(); // can be only CantOpen or Started
void Finish();
void SetAnnotation(const std::string &key, const QString &value);
void SetAnnotationHex(const std::string &key, const QString &value);
inline void ClearAnnotation(const std::string &key) {
SetAnnotation(key, QString());
}

View File

@@ -0,0 +1,30 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "core/event_filter.h"
namespace Core {
EventFilter::EventFilter(
not_null<QObject*> parent,
base::lambda<bool(not_null<QEvent*>)> filter)
: QObject(parent)
, _filter(std::move(filter)) {
parent->installEventFilter(this);
}
bool EventFilter::eventFilter(QObject *watched, QEvent *event) {
return _filter(event);
}
not_null<QObject*> InstallEventFilter(
not_null<QObject*> object,
base::lambda<bool(not_null<QEvent*>)> filter) {
return new EventFilter(object, std::move(filter));
}
} // namespace Core

View File

@@ -0,0 +1,30 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Core {
class EventFilter : public QObject {
public:
EventFilter(
not_null<QObject*> parent,
base::lambda<bool(not_null<QEvent*>)> filter);
protected:
bool eventFilter(QObject *watched, QEvent *event);
private:
base::lambda<bool(not_null<QEvent*>)> _filter;
};
not_null<QObject*> InstallEventFilter(
not_null<QObject*> object,
base::lambda<bool(not_null<QEvent*>)> filter);
} // namespace Core

View File

@@ -29,7 +29,7 @@ QString filedialogDefaultName(
const QString &extension,
const QString &path,
bool skipExistance,
int fileTime) {
TimeId fileTime) {
auto directoryPath = path;
if (directoryPath.isEmpty()) {
if (cDialogLastPath().isEmpty()) {
@@ -40,7 +40,8 @@ QString filedialogDefaultName(
QString base;
if (fileTime) {
base = prefix + ::date(fileTime).toString("_yyyy-MM-dd_HH-mm-ss");
const auto date = ParseDateTime(fileTime);
base = prefix + date.toString("_yyyy-MM-dd_HH-mm-ss");
} else {
struct tm tm;
time_t t = time(NULL);
@@ -91,7 +92,7 @@ void OpenEmailLink(const QString &email) {
}
void OpenWith(const QString &filepath, QPoint menuPosition) {
crl::on_main([=] {
InvokeQueued(QApplication::instance(), [=] {
if (!Platform::File::UnsafeShowOpenWithDropdown(filepath, menuPosition)) {
if (!Platform::File::UnsafeShowOpenWith(filepath)) {
Platform::File::UnsafeLaunch(filepath);
@@ -133,7 +134,7 @@ void GetOpenPath(
const QString &filter,
base::lambda<void(OpenResult &&result)> callback,
base::lambda<void()> failed) {
crl::on_main([=] {
InvokeQueued(QApplication::instance(), [=] {
auto files = QStringList();
auto remoteContent = QByteArray();
const auto success = Platform::FileDialog::Get(
@@ -164,7 +165,7 @@ void GetOpenPaths(
const QString &filter,
base::lambda<void(OpenResult &&result)> callback,
base::lambda<void()> failed) {
crl::on_main([=] {
InvokeQueued(QApplication::instance(), [=] {
auto files = QStringList();
auto remoteContent = QByteArray();
const auto success = Platform::FileDialog::Get(
@@ -192,7 +193,7 @@ void GetWritePath(
const QString &initialPath,
base::lambda<void(QString &&result)> callback,
base::lambda<void()> failed) {
crl::on_main([=] {
InvokeQueued(QApplication::instance(), [=] {
auto file = QString();
if (filedialogGetSaveFile(file, caption, filter, initialPath)) {
if (callback) {
@@ -209,7 +210,7 @@ void GetFolder(
const QString &initialPath,
base::lambda<void(QString &&result)> callback,
base::lambda<void()> failed) {
crl::on_main([=] {
InvokeQueued(QApplication::instance(), [=] {
auto files = QStringList();
auto remoteContent = QByteArray();
const auto success = Platform::FileDialog::Get(

View File

@@ -21,7 +21,7 @@ QString filedialogDefaultName(
const QString &extension,
const QString &path = QString(),
bool skipExistance = false,
int fileTime = 0);
TimeId fileTime = TimeId(0));
QString filedialogNextFilename(
const QString &name,
const QString &cur,

View File

@@ -80,7 +80,7 @@ namespace {
}
}
TimeId myunixtime() {
TimeId LocalUnixtime() {
return (TimeId)time(NULL);
}
@@ -103,31 +103,25 @@ void unixtimeSet(int32 serverTime, bool force) {
DEBUG_LOG(("MTP Info: setting client unixtime to %1").arg(serverTime));
}
unixtimeWasSet = true;
unixtimeDelta = serverTime + 1 - myunixtime();
unixtimeDelta = serverTime + 1 - LocalUnixtime();
DEBUG_LOG(("MTP Info: now unixtimeDelta is %1").arg(unixtimeDelta));
}
_initMsgIdConstants();
}
TimeId unixtime() {
auto result = myunixtime();
auto result = LocalUnixtime();
QReadLocker locker(&unixtimeLock);
return result + unixtimeDelta;
}
TimeId fromServerTime(const MTPint &serverTime) {
QDateTime ParseDateTime(TimeId serverTime) {
if (serverTime <= 0) {
return QDateTime();
}
QReadLocker locker(&unixtimeLock);
return serverTime.v - unixtimeDelta;
}
MTPint toServerTime(const TimeId &clientTime) {
QReadLocker locker(&unixtimeLock);
return MTP_int(clientTime + unixtimeDelta);
}
QDateTime dateFromServerTime(TimeId time) {
return dateFromServerTime(MTP_int(time));
return QDateTime::fromTime_t(serverTime - unixtimeDelta);
}
// Precise timing functions / rand init
@@ -224,7 +218,7 @@ namespace {
_msgIdCoef = float64(0xFFFF0000L) / 1000000000.;
_msStart = 1000LL * static_cast<TimeMs>(ts.tv_sec) + (static_cast<TimeMs>(ts.tv_nsec) / 1000000LL);
#endif
_timeStart = myunixtime();
_timeStart = LocalUnixtime();
srand((uint32)(_msStart & 0xFFFFFFFFL));
}
};
@@ -310,7 +304,7 @@ namespace ThirdParty {
}
bool checkms() {
auto unixms = (myunixtime() - _timeStart) * 1000LL + _msAddToUnixtime;
auto unixms = (LocalUnixtime() - _timeStart) * 1000LL + _msAddToUnixtime;
auto ms = getms(true);
if (ms > unixms + 1000LL) {
_msAddToUnixtime = ((ms - unixms) / 1000LL) * 1000LL;

View File

@@ -139,6 +139,11 @@ reversion_wrapper<Container> reversed(Container &&container) {
return { container };
}
template <typename Value, typename From, typename Till>
inline bool in_range(Value &&value, From &&from, Till &&till) {
return (value >= from) && (value < till);
}
} // namespace base
// using for_const instead of plain range-based for loop to ensure usage of const_iterator
@@ -220,32 +225,14 @@ private:
};
class MTPint;
using TimeId = int32;
TimeId myunixtime();
void unixtimeInit();
void unixtimeSet(TimeId servertime, bool force = false);
void unixtimeSet(TimeId serverTime, bool force = false);
TimeId unixtime();
TimeId fromServerTime(const MTPint &serverTime);
MTPint toServerTime(const TimeId &clientTime);
uint64 msgid();
int32 reqid();
inline QDateTime date(int32 time = -1) {
QDateTime result;
if (time >= 0) result.setTime_t(time);
return result;
}
inline QDateTime dateFromServerTime(const MTPint &time) {
return date(fromServerTime(time));
}
inline QDateTime date(const MTPint &time) {
return dateFromServerTime(time);
}
QDateTime dateFromServerTime(TimeId time);
QDateTime ParseDateTime(TimeId serverTime);
inline void mylocaltime(struct tm * _Tm, const time_t * _Time) {
#ifdef Q_OS_WIN
@@ -520,13 +507,6 @@ inline int ceilclamp(float64 value, int32 step, int32 lowest, int32 highest) {
return qMax(qMin(static_cast<int>(std::ceil(value / step)), highest), lowest);
}
enum ForwardWhatMessages {
ForwardSelectedMessages,
ForwardContextMessage,
ForwardPressedMessage,
ForwardPressedLinkMessage
};
static int32 FullArcLength = 360 * 16;
static int32 QuarterArcLength = (FullArcLength / 4);
static int32 MinArcLength = (FullArcLength / 360);

View File

@@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#define BETA_VERSION_MACRO (0ULL)
constexpr int AppVersion = 1002008;
constexpr str_const AppVersionStr = "1.2.8";
constexpr int AppVersion = 1002009;
constexpr str_const AppVersionStr = "1.2.9";
constexpr bool AppAlphaVersion = true;
constexpr uint64 AppBetaVersion = BETA_VERSION_MACRO;

View File

@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_channel_admins.h"
#include "history/history.h"
namespace Data {
ChannelAdminChanges::ChannelAdminChanges(not_null<ChannelData*> channel)

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_document.h"
#include "data/data_session.h"
#include "lang/lang_keys.h"
#include "inline_bots/inline_bot_layout_item.h"
#include "mainwidget.h"
@@ -14,8 +15,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/media_audio.h"
#include "storage/localstorage.h"
#include "platform/platform_specific.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_media_types.h"
#include "window/window_controller.h"
#include "auth_session.h"
#include "mainwindow.h"
#include "messenger.h"
namespace {
@@ -60,7 +65,13 @@ bool fileIsImage(const QString &name, const QString &mime) {
return false;
}
QString saveFileName(const QString &title, const QString &filter, const QString &prefix, QString name, bool savingAs, const QDir &dir) {
QString FileNameUnsafe(
const QString &title,
const QString &filter,
const QString &prefix,
QString name,
bool savingAs,
const QDir &dir) {
#ifdef Q_OS_WIN
name = name.replace(QRegularExpression(qsl("[\\\\\\/\\:\\*\\?\\\"\\<\\>\\|]")), qsl("_"));
#elif defined Q_OS_MAC
@@ -147,22 +158,31 @@ QString saveFileName(const QString &title, const QString &filter, const QString
return name;
}
bool StickerData::setInstalled() const {
switch (set.type()) {
case mtpc_inputStickerSetID: {
auto it = Auth().data().stickerSets().constFind(set.c_inputStickerSetID().vid.v);
return (it != Auth().data().stickerSets().cend()) && !(it->flags & MTPDstickerSet::Flag::f_archived) && (it->flags & MTPDstickerSet::Flag::f_installed);
} break;
case mtpc_inputStickerSetShortName: {
auto name = qs(set.c_inputStickerSetShortName().vshort_name).toLower();
for (auto it = Auth().data().stickerSets().cbegin(), e = Auth().data().stickerSets().cend(); it != e; ++it) {
if (it->shortName.toLower() == name) {
return !(it->flags & MTPDstickerSet::Flag::f_archived) && (it->flags & MTPDstickerSet::Flag::f_installed);
}
QString FileNameForSave(
const QString &title,
const QString &filter,
const QString &prefix,
QString name,
bool savingAs,
const QDir &dir) {
const auto result = FileNameUnsafe(
title,
filter,
prefix,
name,
savingAs,
dir);
#ifdef Q_OS_WIN
const auto lower = result.trimmed().toLower();
const auto kBadExtensions = { qstr(".lnk"), qstr(".scf") };
const auto kMaskExtension = qsl(".download");
for (const auto extension : kBadExtensions) {
if (lower.endsWith(extension)) {
return result + kMaskExtension;
}
} break;
}
return false;
#endif // Q_OS_WIN
return result;
}
QString documentSaveFilename(const DocumentData *data, bool forceSavingAs = false, const QString already = QString(), const QDir &dir = QDir()) {
@@ -208,7 +228,7 @@ QString documentSaveFilename(const DocumentData *data, bool forceSavingAs = fals
prefix = qsl("doc");
}
return saveFileName(caption, filter, prefix, name, forceSavingAs, dir);
return FileNameForSave(caption, filter, prefix, name, forceSavingAs, dir);
}
void DocumentOpenClickHandler::doOpen(
@@ -244,9 +264,7 @@ void DocumentOpenClickHandler::doOpen(
auto audio = AudioMsgId(data, msgId);
Media::Player::mixer()->play(audio);
Media::Player::Updated().notify(audio);
if (App::main()) {
App::main()->mediaMarkRead(data);
}
data->session()->data().markMediaRead(data);
}
} else if (playMusic) {
auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song);
@@ -273,24 +291,24 @@ void DocumentOpenClickHandler::doOpen(
File::Launch(filepath);
}
}
if (App::main()) App::main()->mediaMarkRead(data);
data->session()->data().markMediaRead(data);
} else if (data->isVoiceMessage() || data->isAudioFile() || data->isVideoFile()) {
auto filepath = location.name();
if (documentIsValidMediaFile(filepath)) {
File::Launch(filepath);
}
if (App::main()) App::main()->mediaMarkRead(data);
data->session()->data().markMediaRead(data);
} else if (data->size < App::kImageSizeLimit) {
if (!data->data().isEmpty() && playAnimation) {
if (action == ActionOnLoadPlayInline && context && context->getMedia()) {
context->getMedia()->playInline();
if (action == ActionOnLoadPlayInline && context) {
data->session()->data().requestAnimationPlayInline(context);
} else {
Messenger::Instance().showDocument(data, context);
}
} else if (location.accessEnable()) {
if (data->isAnimation() || QImageReader(location.name()).canRead()) {
if (action == ActionOnLoadPlayInline && context && context->getMedia()) {
context->getMedia()->playInline();
if (playAnimation || QImageReader(location.name()).canRead()) {
if (playAnimation && action == ActionOnLoadPlayInline && context) {
data->session()->data().requestAnimationPlayInline(context);
} else {
Messenger::Instance().showDocument(data, context);
}
@@ -335,7 +353,9 @@ void DocumentSaveClickHandler::doSave(
bool forceSavingAs) {
if (!data->date) return;
auto filepath = data->filepath(DocumentData::FilePathResolveSaveFromDataSilent, forceSavingAs);
auto filepath = data->filepath(
DocumentData::FilePathResolveSaveFromDataSilent,
forceSavingAs);
if (!filepath.isEmpty() && !forceSavingAs) {
File::OpenWith(filepath, QCursor::pos());
} else {
@@ -344,9 +364,7 @@ void DocumentSaveClickHandler::doSave(
auto filename = filepath.isEmpty() ? QString() : fileinfo.fileName();
auto newfname = documentSaveFilename(data, forceSavingAs, filename, filedir);
if (!newfname.isEmpty()) {
auto action = (filename.isEmpty() || forceSavingAs) ? ActionOnLoadNone : ActionOnLoadOpenWith;
auto actionMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->fullId() : (App::contextItem() ? App::contextItem()->fullId() : FullMsgId());
data->save(newfname, action, actionMsgId);
data->save(newfname, ActionOnLoadNone, FullMsgId());
}
}
}
@@ -361,8 +379,7 @@ void DocumentCancelClickHandler::onClickImpl() const {
if (data->uploading()) {
if (const auto item = App::histItemById(context())) {
App::contextItem(item);
App::main()->cancelUploadLayer();
App::main()->cancelUploadLayer(item);
}
} else {
data->cancel();
@@ -370,35 +387,22 @@ void DocumentCancelClickHandler::onClickImpl() const {
}
VoiceData::~VoiceData() {
if (!waveform.isEmpty() && waveform.at(0) == -1 && waveform.size() > int32(sizeof(TaskId))) {
if (!waveform.isEmpty()
&& waveform[0] == -1
&& waveform.size() > int32(sizeof(TaskId))) {
TaskId taskId = 0;
memcpy(&taskId, waveform.constData() + 1, sizeof(taskId));
Local::cancelTask(taskId);
}
}
DocumentData::DocumentData(DocumentId id, int32 dc, uint64 accessHash, int32 version, const QString &url, const QVector<MTPDocumentAttribute> &attributes)
DocumentData::DocumentData(DocumentId id, not_null<AuthSession*> session)
: id(id)
, _dc(dc)
, _access(accessHash)
, _version(version)
, _url(url) {
setattributes(attributes);
if (_dc && _access) {
_location = Local::readFileLocation(mediaKey());
}
, _session(session) {
}
DocumentData *DocumentData::create(DocumentId id) {
return new DocumentData(id, 0, 0, 0, QString(), QVector<MTPDocumentAttribute>());
}
DocumentData *DocumentData::create(DocumentId id, int32 dc, uint64 accessHash, int32 version, const QVector<MTPDocumentAttribute> &attributes) {
return new DocumentData(id, dc, accessHash, version, QString(), attributes);
}
DocumentData *DocumentData::create(DocumentId id, const QString &url, const QVector<MTPDocumentAttribute> &attributes) {
return new DocumentData(id, 0, 0, 0, url, attributes);
not_null<AuthSession*> DocumentData::session() const {
return _session;
}
void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes) {
@@ -461,15 +465,26 @@ void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes
}
} break;
case mtpc_documentAttributeFilename: {
auto &attribute = attributes[i];
auto remoteFileName = qs(
const auto &attribute = attributes[i];
_filename = qs(
attribute.c_documentAttributeFilename().vfile_name);
// We don't want RTL Override characters in filenames,
// because they introduce a security issue, when a filename
// "Fil[RTLO]gepj.exe" looks like "Filexe.jpeg" being ".exe"
auto rtlOverride = QChar(0x202E);
_filename = std::move(remoteFileName).replace(rtlOverride, "");
// We don't want LTR/RTL mark/embedding/override/isolate chars
// in filenames, because they introduce a security issue, when
// an executable "Fil[x]gepj.exe" may look like "Filexe.jpeg".
QChar controls[] = {
0x200E, // LTR Mark
0x200F, // RTL Mark
0x202A, // LTR Embedding
0x202B, // RTL Embedding
0x202D, // LTR Override
0x202E, // RTL Override
0x2066, // LTR Isolate
0x2067, // RTL Isolate
};
for (const auto ch : controls) {
_filename = std::move(_filename).replace(ch, "_");
}
} break;
}
}
@@ -546,7 +561,10 @@ void DocumentData::performActionOnLoad() {
auto showImage = !isVideoFile() && (size < App::kImageSizeLimit);
auto playVoice = isVoiceMessage() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen);
auto playMusic = isAudioFile() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen);
auto playAnimation = isAnimation() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen) && showImage && item && item->getMedia();
auto playAnimation = isAnimation()
&& (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen)
&& showImage
&& item;
if (auto applyTheme = isTheme()) {
if (!loc.isEmpty() && loc.accessEnable()) {
Messenger::Instance().showDocument(this, item);
@@ -566,7 +584,7 @@ void DocumentData::performActionOnLoad() {
}
} else if (Media::Player::IsStopped(state.state)) {
Media::Player::mixer()->play(AudioMsgId(this, _actionOnLoadMsgId));
if (App::main()) App::main()->mediaMarkRead(this);
_session->data().markMediaRead(this);
}
}
} else if (playMusic) {
@@ -586,8 +604,8 @@ void DocumentData::performActionOnLoad() {
}
} else if (playAnimation) {
if (loaded()) {
if (_actionOnLoad == ActionOnLoadPlayInline && item->getMedia()) {
item->getMedia()->playInline();
if (_actionOnLoad == ActionOnLoadPlayInline && item) {
_session->data().requestAnimationPlayInline(item);
} else {
Messenger::Instance().showDocument(this, item);
}
@@ -602,14 +620,10 @@ void DocumentData::performActionOnLoad() {
if (documentIsValidMediaFile(already)) {
File::Launch(already);
}
if (App::main()) App::main()->mediaMarkRead(this);
_session->data().markMediaRead(this);
} else if (loc.accessEnable()) {
if (showImage && QImageReader(loc.name()).canRead()) {
if (_actionOnLoad == ActionOnLoadPlayInline && item && item->getMedia()) {
item->getMedia()->playInline();
} else {
Messenger::Instance().showDocument(this, item);
}
Messenger::Instance().showDocument(this, item);
} else {
File::Launch(already);
}
@@ -635,7 +649,7 @@ bool DocumentData::loaded(FilePathResolveType type) const {
}
destroyLoaderDelayed();
}
notifyLayoutChanged();
_session->data().notifyDocumentLayoutChanged(this);
}
return !data().isEmpty() || !filepath(type).isEmpty();
}
@@ -643,7 +657,7 @@ bool DocumentData::loaded(FilePathResolveType type) const {
void DocumentData::destroyLoaderDelayed(mtpFileLoader *newValue) const {
_loader->stop();
auto loader = std::unique_ptr<FileLoader>(std::exchange(_loader, newValue));
Auth().downloader().delayedDestroyLoader(std::move(loader));
_session->downloader().delayedDestroyLoader(std::move(loader));
}
bool DocumentData::loading() const {
@@ -690,7 +704,12 @@ bool DocumentData::waitingForAlbum() const {
return uploading() && uploadingData->waitingForAlbum;
}
void DocumentData::save(const QString &toFile, ActionOnLoad action, const FullMsgId &actionMsgId, LoadFromCloudSetting fromCloud, bool autoLoading) {
void DocumentData::save(
const QString &toFile,
ActionOnLoad action,
const FullMsgId &actionMsgId,
LoadFromCloudSetting fromCloud,
bool autoLoading) {
if (loaded(FilePathResolveChecked)) {
auto &l = location(true);
if (!toFile.isEmpty()) {
@@ -731,27 +750,31 @@ void DocumentData::save(const QString &toFile, ActionOnLoad action, const FullMs
if (fromCloud == LoadFromCloudOrLocal) _loader->permitLoadFromCloud();
} else {
status = FileReady;
if (!_access && !_url.isEmpty()) {
if (hasWebLocation()) {
_loader = new mtpFileLoader(&_urlLocation, size, fromCloud, autoLoading);
} else if (!_access && !_url.isEmpty()) {
_loader = new webFileLoader(_url, toFile, fromCloud, autoLoading);
} else {
_loader = new mtpFileLoader(_dc, id, _access, _version, locationType(), toFile, size, (saveToCache() ? LoadToCacheAsWell : LoadToFileOnly), fromCloud, autoLoading);
}
_loader->connect(_loader, SIGNAL(progress(FileLoader*)), App::main(), SLOT(documentLoadProgress(FileLoader*)));
_loader->connect(_loader, SIGNAL(failed(FileLoader*,bool)), App::main(), SLOT(documentLoadFailed(FileLoader*,bool)));
_loader->start();
}
notifyLayoutChanged();
_session->data().notifyDocumentLayoutChanged(this);
}
void DocumentData::cancel() {
if (!loading()) return;
if (!loading()) {
return;
}
auto loader = std::unique_ptr<FileLoader>(std::exchange(_loader, CancelledMtpFileLoader));
loader->cancel();
loader->stop();
Auth().downloader().delayedDestroyLoader(std::move(loader));
notifyLayoutChanged();
_session->downloader().delayedDestroyLoader(std::move(loader));
_session->data().notifyDocumentLayoutChanged(this);
if (auto main = App::main()) {
main->documentLoadProgress(this);
}
@@ -759,19 +782,6 @@ void DocumentData::cancel() {
_actionOnLoad = ActionOnLoadNone;
}
void DocumentData::notifyLayoutChanged() const {
auto &items = App::documentItems();
for (auto item : items.value(const_cast<DocumentData*>(this))) {
Auth().data().markItemLayoutChanged(item);
}
if (auto items = InlineBots::Layout::documentItems()) {
for (auto item : items->value(const_cast<DocumentData*>(this))) {
item->layoutChanged();
}
}
}
VoiceWaveform documentWaveformDecode(const QByteArray &encoded5bit) {
auto bitsCount = static_cast<int>(encoded5bit.size() * 8);
auto valuesCount = bitsCount / 5;
@@ -869,6 +879,31 @@ QString DocumentData::filepath(FilePathResolveType type, bool forceSavingAs) con
return result;
}
bool DocumentData::isStickerSetInstalled() const {
Expects(sticker() != nullptr);
const auto &set = sticker()->set;
const auto &sets = _session->data().stickerSets();
switch (set.type()) {
case mtpc_inputStickerSetID: {
auto it = sets.constFind(set.c_inputStickerSetID().vid.v);
return (it != sets.cend())
&& !(it->flags & MTPDstickerSet::Flag::f_archived)
&& (it->flags & MTPDstickerSet::Flag::f_installed_date);
} break;
case mtpc_inputStickerSetShortName: {
auto name = qs(set.c_inputStickerSetShortName().vshort_name).toLower();
for (auto it = sets.cbegin(), e = sets.cend(); it != e; ++it) {
if (it->shortName.toLower() == name) {
return !(it->flags & MTPDstickerSet::Flag::f_archived)
&& (it->flags & MTPDstickerSet::Flag::f_installed_date);
}
}
} break;
}
return false;
}
ImagePtr DocumentData::makeReplyPreview() {
if (replyPreview->isNull() && !thumb->isNull()) {
if (thumb->loaded()) {
@@ -888,6 +923,109 @@ ImagePtr DocumentData::makeReplyPreview() {
return replyPreview;
}
StickerData *DocumentData::sticker() const {
return (type == StickerDocument)
? static_cast<StickerData*>(_additional.get())
: nullptr;
}
void DocumentData::checkSticker() {
const auto data = sticker();
if (!data) return;
automaticLoad(nullptr);
if (data->img->isNull() && loaded()) {
if (_data.isEmpty()) {
const FileLocation &loc(location(true));
if (loc.accessEnable()) {
data->img = ImagePtr(loc.name());
loc.accessDisable();
}
} else {
data->img = ImagePtr(_data);
}
}
}
SongData *DocumentData::song() {
return isSong()
? static_cast<SongData*>(_additional.get())
: nullptr;
}
const SongData *DocumentData::song() const {
return const_cast<DocumentData*>(this)->song();
}
VoiceData *DocumentData::voice() {
return isVoiceMessage()
? static_cast<VoiceData*>(_additional.get())
: nullptr;
}
const VoiceData *DocumentData::voice() const {
return const_cast<DocumentData*>(this)->voice();
}
bool DocumentData::hasRemoteLocation() const {
return (_dc != 0 && _access != 0);
}
bool DocumentData::hasWebLocation() const {
return _urlLocation.dc() != 0 && _urlLocation.accessHash() != 0;
}
bool DocumentData::isValid() const {
return hasRemoteLocation() || hasWebLocation() || !_url.isEmpty();
}
MTPInputDocument DocumentData::mtpInput() const {
if (_access) {
return MTP_inputDocument(
MTP_long(id),
MTP_long(_access));
}
return MTP_inputDocumentEmpty();
}
QString DocumentData::filename() const {
return _filename;
}
QString DocumentData::mimeString() const {
return _mimeString;
}
bool DocumentData::hasMimeType(QLatin1String mime) const {
return !_mimeString.compare(mime, Qt::CaseInsensitive);
}
void DocumentData::setMimeString(const QString &mime) {
_mimeString = mime;
}
MediaKey DocumentData::mediaKey() const {
return ::mediaKey(locationType(), _dc, id, _version);
}
QString DocumentData::composeNameString() const {
if (auto songData = song()) {
return ComposeNameString(
_filename,
songData->title,
songData->performer);
}
return ComposeNameString(_filename, QString(), QString());
}
LocationType DocumentData::locationType() const {
return isVoiceMessage()
? AudioFileLocation
: isVideoFile()
? VideoFileLocation
: DocumentFileLocation;
}
bool DocumentData::isVoiceMessage() const {
return (type == VoiceDocument);
}
@@ -971,13 +1109,15 @@ bool DocumentData::setRemoteVersion(int32 version) {
}
void DocumentData::setRemoteLocation(int32 dc, uint64 access) {
_dc = dc;
_access = access;
if (isValid()) {
if (_location.check()) {
Local::writeFileLocation(mediaKey(), _location);
} else {
_location = Local::readFileLocation(mediaKey());
if (_dc != dc || _access != access) {
_dc = dc;
_access = access;
if (isValid()) {
if (_location.check()) {
Local::writeFileLocation(mediaKey(), _location);
} else {
_location = Local::readFileLocation(mediaKey());
}
}
}
}
@@ -986,6 +1126,10 @@ void DocumentData::setContentUrl(const QString &url) {
_url = url;
}
void DocumentData::setWebLocation(const WebFileLocation &location) {
_urlLocation = location;
}
void DocumentData::collectLocalData(DocumentData *local) {
if (local == this) return;

View File

@@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_types.h"
class AuthSession;
inline uint64 mediaMix32To64(int32 a, int32 b) {
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32)
| uint64(*reinterpret_cast<uint32*>(&b));
@@ -42,10 +44,7 @@ struct DocumentAdditionalData {
struct StickerData : public DocumentAdditionalData {
ImagePtr img;
QString alt;
MTPInputStickerSet set = MTP_inputStickerSetEmpty();
bool setInstalled() const;
StorageImageLocation loc; // doc thumb location
};
@@ -72,17 +71,9 @@ class Document;
class DocumentData {
public:
static DocumentData *create(DocumentId id);
static DocumentData *create(
DocumentId id,
int32 dc,
uint64 accessHash,
int32 version,
const QVector<MTPDocumentAttribute> &attributes);
static DocumentData *create(
DocumentId id,
const QString &url,
const QVector<MTPDocumentAttribute> &attributes);
DocumentData(DocumentId id, not_null<AuthSession*> session);
not_null<AuthSession*> session() const;
void setattributes(
const QVector<MTPDocumentAttribute> &attributes);
@@ -130,44 +121,14 @@ public:
void forget();
ImagePtr makeReplyPreview();
StickerData *sticker() {
return (type == StickerDocument)
? static_cast<StickerData*>(_additional.get())
: nullptr;
}
void checkSticker() {
StickerData *s = sticker();
if (!s) return;
StickerData *sticker() const;
void checkSticker();
bool isStickerSetInstalled() const;
SongData *song();
const SongData *song() const;
VoiceData *voice();
const VoiceData *voice() const;
automaticLoad(nullptr);
if (s->img->isNull() && loaded()) {
if (_data.isEmpty()) {
const FileLocation &loc(location(true));
if (loc.accessEnable()) {
s->img = ImagePtr(loc.name());
loc.accessDisable();
}
} else {
s->img = ImagePtr(_data);
}
}
}
SongData *song() {
return isSong()
? static_cast<SongData*>(_additional.get())
: nullptr;
}
const SongData *song() const {
return const_cast<DocumentData*>(this)->song();
}
VoiceData *voice() {
return isVoiceMessage()
? static_cast<VoiceData*>(_additional.get())
: nullptr;
}
const VoiceData *voice() const {
return const_cast<DocumentData*>(this)->voice();
}
bool isVoiceMessage() const;
bool isVideoMessage() const;
bool isSong() const;
@@ -187,20 +148,11 @@ public:
bool setRemoteVersion(int32 version); // Returns true if version has changed.
void setRemoteLocation(int32 dc, uint64 access);
void setContentUrl(const QString &url);
bool hasRemoteLocation() const {
return (_dc != 0 && _access != 0);
}
bool isValid() const {
return hasRemoteLocation() || !_url.isEmpty();
}
MTPInputDocument mtpInput() const {
if (_access) {
return MTP_inputDocument(
MTP_long(id),
MTP_long(_access));
}
return MTP_inputDocumentEmpty();
}
void setWebLocation(const WebFileLocation &location);
bool hasRemoteLocation() const;
bool hasWebLocation() const;
bool isValid() const;
MTPInputDocument mtpInput() const;
// When we have some client-side generated document
// (for example for displaying an external inline bot result)
@@ -208,18 +160,18 @@ public:
// to (this) received from the server "same" document.
void collectLocalData(DocumentData *local);
QString filename() const {
return _filename;
}
QString mimeString() const {
return _mimeString;
}
bool hasMimeType(QLatin1String mime) const {
return !_mimeString.compare(mime, Qt::CaseInsensitive);
}
void setMimeString(const QString &mime) {
_mimeString = mime;
}
QString filename() const;
QString mimeString() const;
bool hasMimeType(QLatin1String mime) const;
void setMimeString(const QString &mime);
MediaKey mediaKey() const;
static QString ComposeNameString(
const QString &filename,
const QString &songTitle,
const QString &songPerformer);
QString composeNameString() const;
~DocumentData();
@@ -234,44 +186,12 @@ public:
std::unique_ptr<Data::UploadState> uploadingData;
int32 md5[8];
MediaKey mediaKey() const {
return ::mediaKey(locationType(), _dc, id, _version);
}
static QString ComposeNameString(
const QString &filename,
const QString &songTitle,
const QString &songPerformer);
QString composeNameString() const {
if (auto songData = song()) {
return ComposeNameString(
_filename,
songData->title,
songData->performer);
}
return ComposeNameString(_filename, QString(), QString());
}
private:
DocumentData(
DocumentId id,
int32 dc,
uint64 accessHash,
int32 version,
const QString &url,
const QVector<MTPDocumentAttribute> &attributes);
friend class Serialize::Document;
LocationType locationType() const {
return isVoiceMessage()
? AudioFileLocation
: isVideoFile()
? VideoFileLocation
: DocumentFileLocation;
}
LocationType locationType() const;
void destroyLoaderDelayed(mtpFileLoader *newValue = nullptr) const;
// Two types of location: from MTProto by dc+access+version or from web by url
int32 _dc = 0;
@@ -280,6 +200,9 @@ private:
QString _url;
QString _filename;
QString _mimeString;
WebFileLocation _urlLocation;
not_null<AuthSession*> _session;
FileLocation _location;
QByteArray _data;
@@ -290,11 +213,6 @@ private:
FullMsgId _actionOnLoadMsgId;
mutable FileLoader *_loader = nullptr;
void notifyLayoutChanged() const;
void destroyLoaderDelayed(
mtpFileLoader *newValue = nullptr) const;
};
VoiceWaveform documentWaveformDecode(const QByteArray &encoded5bit);
@@ -370,7 +288,7 @@ protected:
};
QString saveFileName(
QString FileNameForSave(
const QString &title,
const QString &filter,
const QString &prefix,

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/input_fields.h"
#include "chat_helpers/message_field.h"
#include "history/history.h"
#include "history/history_widget.h"
#include "mainwidget.h"
#include "storage/localstorage.h"
@@ -18,11 +19,28 @@ namespace {
} // namespace
Draft::Draft(const Ui::FlatTextarea *field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequestId)
: textWithTags(field->getTextWithTags())
, msgId(msgId)
, cursor(field)
, previewCancelled(previewCancelled) {
Draft::Draft(
const TextWithTags &textWithTags,
MsgId msgId,
const MessageCursor &cursor,
bool previewCancelled,
mtpRequestId saveRequestId)
: textWithTags(textWithTags)
, msgId(msgId)
, cursor(cursor)
, previewCancelled(previewCancelled)
, saveRequestId(saveRequestId) {
}
Draft::Draft(
not_null<const Ui::FlatTextarea*> field,
MsgId msgId,
bool previewCancelled,
mtpRequestId saveRequestId)
: textWithTags(field->getTextWithTags())
, msgId(msgId)
, cursor(field)
, previewCancelled(previewCancelled) {
}
void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) {
@@ -31,7 +49,7 @@ void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) {
auto textWithTags = TextWithTags { TextUtilities::ApplyEntities(text), ConvertEntitiesToTextTags(text.entities) };
auto replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : MsgId(0);
auto cloudDraft = std::make_unique<Draft>(textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), draft.is_no_webpage());
cloudDraft->date = ::date(draft.vdate);
cloudDraft->date = draft.vdate.v;
history->setCloudDraft(std::move(cloudDraft));
history->createLocalDraftFromCloud();

View File

@@ -17,18 +17,20 @@ void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft);
void clearPeerCloudDraft(PeerId peerId);
struct Draft {
Draft() {
}
Draft(const TextWithTags &textWithTags, MsgId msgId, const MessageCursor &cursor, bool previewCancelled, mtpRequestId saveRequestId = 0)
: textWithTags(textWithTags)
, msgId(msgId)
, cursor(cursor)
, previewCancelled(previewCancelled)
, saveRequestId(saveRequestId) {
}
Draft(const Ui::FlatTextarea *field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequestId = 0);
Draft() = default;
Draft(
const TextWithTags &textWithTags,
MsgId msgId,
const MessageCursor &cursor,
bool previewCancelled,
mtpRequestId saveRequestId = 0);
Draft(
not_null<const Ui::FlatTextarea*> field,
MsgId msgId,
bool previewCancelled,
mtpRequestId saveRequestId = 0);
QDateTime date;
TimeId date = 0;
TextWithTags textWithTags;
MsgId msgId = 0; // replyToId for message draft, editMsgId for edit draft
MessageCursor cursor;

View File

@@ -0,0 +1,474 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_feed.h"
#include "data/data_session.h"
#include "dialogs/dialogs_key.h"
#include "history/history.h"
#include "history/history_item.h"
#include "lang/lang_keys.h"
#include "storage/storage_facade.h"
#include "storage/storage_feed_messages.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "mainwidget.h"
namespace Data {
// #feed
//MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position) {
// Expects(position.type() == mtpc_feedPosition);
//
// const auto &data = position.c_feedPosition();
// return MessagePosition(data.vdate.v, FullMsgId(
// peerToChannel(peerFromMTP(data.vpeer)),
// data.vid.v));
//}
Feed::Feed(FeedId id, not_null<Data::Session*> parent)
: Entry(this)
, _id(id)
, _parent(parent)
, _name(lang(lng_feed_name)) {
indexNameParts();
}
FeedId Feed::id() const {
return _id;
}
void Feed::indexNameParts() {
_nameWords.clear();
_nameFirstLetters.clear();
auto toIndexList = QStringList();
auto appendToIndex = [&](const QString &value) {
if (!value.isEmpty()) {
toIndexList.push_back(TextUtilities::RemoveAccents(value));
}
};
appendToIndex(_name);
const auto appendTranslit = !toIndexList.isEmpty()
&& cRussianLetters().match(toIndexList.front()).hasMatch();
if (appendTranslit) {
appendToIndex(translitRusEng(toIndexList.front()));
}
auto toIndex = toIndexList.join(' ');
toIndex += ' ' + rusKeyboardLayoutSwitch(toIndex);
const auto namesList = TextUtilities::PrepareSearchWords(toIndex);
for (const auto &name : namesList) {
_nameWords.insert(name);
_nameFirstLetters.insert(name[0]);
}
}
void Feed::registerOne(not_null<ChannelData*> channel) {
const auto history = App::history(channel);
if (!base::contains(_channels, history)) {
const auto invisible = (_channels.size() < 2);
_channels.push_back(history);
_parent->session().storage().invalidate(
Storage::FeedMessagesInvalidate(_id));
if (history->lastMessageKnown()) {
if (const auto last = history->lastMessage()) {
if (justUpdateLastMessage(last)) {
updateChatListEntry();
}
}
} else if (lastMessageKnown()) {
_parent->session().api().requestDialogEntry(history);
}
if (unreadCountKnown()) {
if (history->unreadCountKnown()) {
// If history unreadCount is known that means that we've
// already had the channel information and if it was in the
// feed already (not yet known) it wouldn't get here.
// That means here we get if we add a new channel to feed.
if (const auto count = history->unreadCount()) {
unreadCountChanged(count, history->mute() ? count : 0);
}
} else if (!_settingChannels) {
_parent->session().api().requestDialogEntry(this);
}
}
if (invisible && _channels.size() > 1) {
updateChatListExistence();
for (const auto history : _channels) {
history->updateChatListExistence();
}
} else {
history->updateChatListExistence();
}
_parent->notifyFeedUpdated(this, FeedUpdateFlag::Channels);
}
}
void Feed::unregisterOne(not_null<ChannelData*> channel) {
const auto history = App::history(channel);
const auto i = ranges::remove(_channels, history);
if (i != end(_channels)) {
const auto visible = (_channels.size() > 1);
_channels.erase(i, end(_channels));
_parent->session().storage().remove(
Storage::FeedMessagesRemoveAll(_id, channel->bareId()));
if (lastMessageKnown()) {
if (const auto last = lastMessage()) {
if (last->history() == history) {
recountLastMessage();
}
}
}
if (unreadCountKnown()) {
if (history->unreadCountKnown()) {
if (const auto delta = -history->unreadCount()) {
unreadCountChanged(delta, history->mute() ? delta : 0);
}
} else {
_parent->session().api().requestDialogEntry(this);
}
}
if (visible && _channels.size() < 2) {
updateChatListExistence();
for (const auto history : _channels) {
history->updateChatListExistence();
}
} else {
history->updateChatListExistence();
}
_parent->notifyFeedUpdated(this, FeedUpdateFlag::Channels);
}
}
void Feed::updateLastMessage(not_null<HistoryItem*> item) {
if (justUpdateLastMessage(item)) {
if (_lastMessage && *_lastMessage) {
setChatsListDate(ItemDateTime(*_lastMessage));
}
}
}
void Feed::loadUserpic() {
constexpr auto kPaintUserpicsCount = 4;
auto load = kPaintUserpicsCount;
for (const auto channel : _channels) {
channel->peer->loadUserpic();
if (!--load) {
break;
}
}
}
void Feed::paintUserpic(
Painter &p,
int x,
int y,
int size) const {
const auto small = (size - st::lineWidth) / 2;
const auto delta = size - small;
auto index = 0;
for (const auto channel : _channels) {
channel->peer->paintUserpic(p, x, y, small);
switch (++index) {
case 1:
case 3: x += delta; break;
case 2: x -= delta; y += delta; break;
case 4: return;
}
}
}
const std::vector<not_null<History*>> &Feed::channels() const {
return _channels;
}
int32 Feed::channelsHash() const {
const auto ordered = ranges::view::all(
_channels
) | ranges::view::transform([](not_null<History*> history) {
return history->peer->bareId();
}) | ranges::to_vector | ranges::action::sort;
return Api::CountHash(ordered);
}
bool Feed::channelsLoaded() const {
return _channelsLoaded;
}
void Feed::setChannelsLoaded(bool loaded) {
if (_channelsLoaded != loaded) {
_channelsLoaded = loaded;
_parent->notifyFeedUpdated(this, FeedUpdateFlag::Channels);
}
}
void Feed::setChannels(std::vector<not_null<ChannelData*>> channels) {
const auto remove = ranges::view::all(
_channels
) | ranges::view::transform([](not_null<History*> history) {
return not_null<ChannelData*>(history->peer->asChannel());
}) | ranges::view::filter([&](not_null<ChannelData*> channel) {
return !base::contains(channels, channel);
}) | ranges::to_vector;
const auto add = ranges::view::all(
channels
) | ranges::view::filter([&](not_null<ChannelData*> channel) {
return ranges::find(
_channels,
channel.get(),
[](auto history) { return history->peer->asChannel(); }
) == end(_channels);
}) | ranges::view::transform([](ChannelData *channel) {
return not_null<ChannelData*>(channel);
}) | ranges::to_vector;
changeChannelsList(add, remove);
setChannelsLoaded(true);
}
void Feed::changeChannelsList(
const std::vector<not_null<ChannelData*>> &add,
const std::vector<not_null<ChannelData*>> &remove) {
_settingChannels = true;
const auto restore = gsl::finally([&] { _settingChannels = false; });
for (const auto channel : remove) {
channel->clearFeed();
}
// We assume the last message was correct before requesting the list.
// So we save it and don't allow channels from the list to change it.
// After that we restore it.
const auto oldLastMessage = base::take(_lastMessage);
for (const auto channel : add) {
_lastMessage = base::none;
channel->setFeed(this);
}
_lastMessage = oldLastMessage;
}
bool Feed::justUpdateLastMessage(not_null<HistoryItem*> item) {
if (!_lastMessage) {
return false;
} else if (*_lastMessage
&& item->position() <= (*_lastMessage)->position()) {
return false;
}
_lastMessage = item;
return true;
}
void Feed::messageRemoved(not_null<HistoryItem*> item) {
if (lastMessage() == item) {
recountLastMessage();
}
}
void Feed::historyCleared(not_null<History*> history) {
if (const auto last = lastMessage()) {
if (last->history() == history) {
messageRemoved(last);
}
}
}
void Feed::recountLastMessage() {
_lastMessage = base::none;
for (const auto history : _channels) {
if (!history->lastMessageKnown()) {
_parent->session().api().requestDialogEntry(this);
return;
}
}
setLastMessageFromChannels();
}
void Feed::setLastMessageFromChannels() {
_lastMessage = nullptr;
for (const auto history : _channels) {
if (const auto last = history->lastMessage()) {
justUpdateLastMessage(last);
}
}
updateChatsListDate();
}
void Feed::updateChatsListDate() {
if (_lastMessage && *_lastMessage) {
setChatsListDate(ItemDateTime(*_lastMessage));
}
}
HistoryItem *Feed::lastMessage() const {
return _lastMessage ? *_lastMessage : nullptr;
}
bool Feed::lastMessageKnown() const {
return !!_lastMessage;
}
int Feed::unreadCount() const {
return _unreadCount ? *_unreadCount : 0;
}
rpl::producer<int> Feed::unreadCountValue() const {
return rpl::single(
unreadCount()
) | rpl::then(_unreadCountChanges.events());
}
bool Feed::unreadCountKnown() const {
return !!_unreadCount;
}
// #feed
//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::changedInChatListHook(Dialogs::Mode list, bool added) {
if (list == Dialogs::Mode::All && unreadCount()) {
const auto mutedCount = _unreadMutedCount;
const auto nonMutedCount = unreadCount() - mutedCount;
const auto mutedDelta = added ? mutedCount : -mutedCount;
const auto nonMutedDelta = added ? nonMutedCount : -nonMutedCount;
App::histories().unreadIncrement(nonMutedDelta, false);
App::histories().unreadIncrement(mutedDelta, true);
}
}
void Feed::setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount) {
if (unreadCountKnown()
&& (*_unreadCount == unreadNonMutedCount + unreadMutedCount)
&& (_unreadMutedCount == unreadMutedCount)) {
return;
}
const auto unreadNonMutedCountDelta = _unreadCount | [&](int count) {
return unreadNonMutedCount - (count - _unreadMutedCount);
};
const auto unreadMutedCountDelta = _unreadCount | [&](int count) {
return unreadMutedCount - _unreadMutedCount;
};
_unreadCount = unreadNonMutedCount + unreadMutedCount;
_unreadMutedCount = unreadMutedCount;
_unreadCountChanges.fire(unreadCount());
updateChatListEntry();
if (inChatList(Dialogs::Mode::All)) {
App::histories().unreadIncrement(
unreadNonMutedCountDelta ? *unreadNonMutedCountDelta : unreadNonMutedCount,
false);
App::histories().unreadIncrement(
unreadMutedCountDelta ? *unreadMutedCountDelta : unreadMutedCount,
true);
}
}
void Feed::setUnreadPosition(const MessagePosition &position) {
if (_unreadPosition.current() < position) {
_unreadPosition = position;
}
}
void Feed::unreadCountChanged(
int unreadCountDelta,
int mutedCountDelta) {
if (!unreadCountKnown()) {
return;
}
accumulate_max(unreadCountDelta, -*_unreadCount);
*_unreadCount += unreadCountDelta;
mutedCountDelta = snap(
mutedCountDelta,
-_unreadMutedCount,
*_unreadCount - _unreadMutedCount);
_unreadMutedCount += mutedCountDelta;
_unreadCountChanges.fire(unreadCount());
updateChatListEntry();
if (inChatList(Dialogs::Mode::All)) {
App::histories().unreadIncrement(
unreadCountDelta,
false);
App::histories().unreadMuteChanged(mutedCountDelta, true);
}
}
MessagePosition Feed::unreadPosition() const {
return _unreadPosition.current();
}
rpl::producer<MessagePosition> Feed::unreadPositionChanges() const {
return _unreadPosition.changes();
}
bool Feed::toImportant() const {
return false; // TODO feeds workmode
}
bool Feed::shouldBeInChatList() const {
return _channels.size() > 1;
}
int Feed::chatListUnreadCount() const {
return unreadCount();
}
bool Feed::chatListMutedBadge() const {
return _unreadCount ? (*_unreadCount <= _unreadMutedCount) : false;
}
HistoryItem *Feed::chatsListItem() const {
return lastMessage();
}
const QString &Feed::chatsListName() const {
return _name;
}
const base::flat_set<QString> &Feed::chatsListNameWords() const {
return _nameWords;
}
const base::flat_set<QChar> &Feed::chatsListFirstLetters() const {
return _nameFirstLetters;
}
} // namespace Data

View File

@@ -0,0 +1,113 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "dialogs/dialogs_entry.h"
#include "data/data_messages.h"
class ChannelData;
namespace Data {
class Session;
class Feed;
enum class FeedUpdateFlag {
Channels,
ChannelPhoto,
};
struct FeedUpdate {
not_null<Data::Feed*> feed;
FeedUpdateFlag flag;
};
//MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position); // #feed
class Feed : public Dialogs::Entry {
public:
static constexpr auto kId = 1;
static constexpr auto kChannelsLimit = 1000;
Feed(FeedId id, not_null<Data::Session*> parent);
FeedId id() const;
void registerOne(not_null<ChannelData*> channel);
void unregisterOne(not_null<ChannelData*> channel);
void updateLastMessage(not_null<HistoryItem*> item);
void messageRemoved(not_null<HistoryItem*> item);
void historyCleared(not_null<History*> history);
//void applyDialog(const MTPDdialogFeed &data); // #feed
void setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount);
void setUnreadPosition(const MessagePosition &position);
void unreadCountChanged(
int unreadCountDelta,
int mutedCountDelta);
rpl::producer<int> unreadCountValue() const;
MessagePosition unreadPosition() const;
rpl::producer<MessagePosition> unreadPositionChanges() const;
HistoryItem *lastMessage() const;
bool lastMessageKnown() const;
int unreadCount() const;
bool unreadCountKnown() const;
bool toImportant() const override;
bool shouldBeInChatList() const override;
int chatListUnreadCount() const override;
bool chatListMutedBadge() const override;
HistoryItem *chatsListItem() const override;
const QString &chatsListName() const override;
const base::flat_set<QString> &chatsListNameWords() const override;
const base::flat_set<QChar> &chatsListFirstLetters() const override;
void changedInChatListHook(Dialogs::Mode list, bool added) override;
void loadUserpic() override;
void paintUserpic(
Painter &p,
int x,
int y,
int size) const override;
const std::vector<not_null<History*>> &channels() const;
int32 channelsHash() const;
bool channelsLoaded() const;
void setChannelsLoaded(bool loaded);
void setChannels(std::vector<not_null<ChannelData*>> channels);
private:
void indexNameParts();
void recountLastMessage();
void setLastMessageFromChannels();
bool justUpdateLastMessage(not_null<HistoryItem*> item);
void updateChatsListDate();
void changeChannelsList(
const std::vector<not_null<ChannelData*>> &add,
const std::vector<not_null<ChannelData*>> &remove);
FeedId _id = 0;
not_null<Data::Session*> _parent;
std::vector<not_null<History*>> _channels;
bool _settingChannels = false;
bool _channelsLoaded = false;
QString _name;
base::flat_set<QString> _nameWords;
base::flat_set<QChar> _nameFirstLetters;
base::optional<HistoryItem*> _lastMessage;
rpl::variable<MessagePosition> _unreadPosition;
base::optional<int> _unreadCount;
rpl::event_stream<int> _unreadCountChanges;
int _unreadMutedCount = 0;
};
} // namespace Data

View File

@@ -0,0 +1,103 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_feed_messages.h"
#include "apiwrap.h"
#include "auth_session.h"
#include "data/data_session.h"
#include "storage/storage_feed_messages.h"
namespace Data {
rpl::producer<MessagesSlice> FeedMessagesViewer(
Storage::FeedMessagesKey key,
int limitBefore,
int limitAfter) {
Expects(IsServerMsgId(key.position.fullId.msg)
|| (key.position.fullId.msg == 0));
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
const auto builder = lifetime.make_state<MessagesSliceBuilder>(
key.position,
limitBefore,
limitAfter);
const auto feed = Auth().data().feed(key.feedId);
using AroundData = MessagesSliceBuilder::AroundData;
const auto requestMediaAround = [=](const AroundData &data) {
if (data.aroundId || !key.position) {
//Auth().api().requestFeedMessages( // #feed
// feed,
// data.aroundId,
// data.direction);
}
};
builder->insufficientAround(
) | rpl::start_with_next(requestMediaAround, lifetime);
const auto pushNextSnapshot = [=] {
consumer.put_next(builder->snapshot());
};
using SliceUpdate = Storage::FeedMessagesSliceUpdate;
Auth().storage().feedMessagesSliceUpdated(
) | rpl::filter([=](const SliceUpdate &update) {
return (update.feedId == key.feedId);
}) | rpl::filter([=](const SliceUpdate &update) {
return builder->applyUpdate(update.data);
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
using OneRemoved = Storage::FeedMessagesRemoveOne;
Auth().storage().feedMessagesOneRemoved(
) | rpl::filter([=](const OneRemoved &update) {
return (update.feedId == key.feedId);
}) | rpl::filter([=](const OneRemoved &update) {
return builder->removeOne(update.messageId);
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
using AllRemoved = Storage::FeedMessagesRemoveAll;
Auth().storage().feedMessagesAllRemoved(
) | rpl::filter([=](const AllRemoved &update) {
return (update.feedId == key.feedId);
}) | rpl::filter([=](const AllRemoved &update) {
return builder->removeFromChannel(update.channelId);
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
using Invalidate = Storage::FeedMessagesInvalidate;
Auth().storage().feedMessagesInvalidated(
) | rpl::filter([=](const Invalidate &update) {
return (update.feedId == key.feedId);
}) | rpl::filter([=] {
return builder->invalidated();
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
using InvalidateBottom = Storage::FeedMessagesInvalidateBottom;
Auth().storage().feedMessagesBottomInvalidated(
) | rpl::filter([=](const InvalidateBottom &update) {
return (update.feedId == key.feedId);
}) | rpl::filter([=] {
return builder->bottomInvalidated();
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
using Result = Storage::FeedMessagesResult;
Auth().storage().query(Storage::FeedMessagesQuery(
key,
limitBefore,
limitAfter
)) | rpl::filter([=](const Result &result) {
return builder->applyInitial(result);
}) | rpl::start_with_next_done(
pushNextSnapshot,
[=] { builder->checkInsufficient(); },
lifetime);
return lifetime;
};
}
} // namespace Data

View File

@@ -0,0 +1,24 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_feed.h"
#include "data/data_messages.h"
namespace Storage {
struct FeedMessagesKey;
} // namespace Storage
namespace Data {
rpl::producer<MessagesSlice> FeedMessagesViewer(
Storage::FeedMessagesKey key,
int limitBefore,
int limitAfter);
} // namespace Data

View File

@@ -0,0 +1,140 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_groups.h"
#include "history/history_item.h"
#include "data/data_media_types.h"
#include "data/data_session.h"
namespace Data {
namespace {
constexpr auto kMaxItemsInGroup = 10;
} // namespace
Groups::Groups(not_null<Session*> data) : _data(data) {
}
bool Groups::isGrouped(not_null<HistoryItem*> item) const {
if (!item->groupId()) {
return false;
}
const auto media = item->media();
return media && media->canBeGrouped();
}
void Groups::registerMessage(not_null<HistoryItem*> item) {
if (!isGrouped(item)) {
return;
}
const auto i = _groups.emplace(item->groupId(), Group()).first;
auto &items = i->second.items;
if (items.size() < kMaxItemsInGroup) {
items.insert(findPositionForItem(items, item), item);
if (items.size() > 1) {
refreshViews(items);
}
}
}
void Groups::unregisterMessage(not_null<const HistoryItem*> item) {
const auto groupId = item->groupId();
if (!groupId) {
return;
}
const auto i = _groups.find(groupId);
if (i != end(_groups)) {
auto &items = i->second.items;
const auto removed = ranges::remove(items, item);
const auto last = end(items);
if (removed != last) {
items.erase(removed, last);
if (!items.empty()) {
refreshViews(items);
} else {
_groups.erase(i);
}
}
}
}
void Groups::refreshMessage(not_null<HistoryItem*> item) {
if (!isGrouped(item)) {
unregisterMessage(item);
return;
}
if (!IsServerMsgId(item->id)) {
return;
}
const auto groupId = item->groupId();
const auto i = _groups.find(groupId);
if (i == end(_groups)) {
registerMessage(item);
return;
}
auto &items = i->second.items;
const auto position = findPositionForItem(items, item);
auto current = ranges::find(items, item);
if (current == end(items)) {
items.insert(position, item);
} else if (position == current + 1) {
return;
} else if (position > current + 1) {
for (++current; current != position; ++current) {
std::swap(*(current - 1), *current);
}
} else if (position < current) {
for (; current != position; --current) {
std::swap(*(current - 1), *current);
}
} else {
Unexpected("Position of item in Groups::refreshMessage().");
}
refreshViews(items);
}
HistoryItemsList::const_iterator Groups::findPositionForItem(
const HistoryItemsList &group,
not_null<HistoryItem*> item) {
const auto itemId = item->id;
const auto last = end(group);
if (!IsServerMsgId(itemId)) {
return last;
}
for (auto result = begin(group); result != last; ++result) {
const auto alreadyId = (*result)->id;
if (IsServerMsgId(alreadyId) && alreadyId > itemId) {
return result;
}
}
return last;
}
const Group *Groups::find(not_null<HistoryItem*> item) const {
const auto groupId = item->groupId();
if (!groupId) {
return nullptr;
}
const auto i = _groups.find(groupId);
if (i != _groups.end()) {
const auto &result = i->second;
if (result.items.size() > 1) {
return &result;
}
}
return nullptr;
}
void Groups::refreshViews(const HistoryItemsList &items) {
for (const auto item : items) {
_data->requestItemViewRefresh(item);
}
}
} // namespace Data

View File

@@ -0,0 +1,44 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_types.h"
namespace Data {
class Session;
struct Group {
HistoryItemsList items;
};
class Groups {
public:
Groups(not_null<Session*> data);
bool isGrouped(not_null<HistoryItem*> item) const;
void registerMessage(not_null<HistoryItem*> item);
void unregisterMessage(not_null<const HistoryItem*> item);
void refreshMessage(not_null<HistoryItem*> item);
const Group *find(not_null<HistoryItem*> item) const;
private:
HistoryItemsList::const_iterator findPositionForItem(
const HistoryItemsList &group,
not_null<HistoryItem*> item);
void refreshViews(const HistoryItemsList &items);
not_null<Session*> _data;
std::map<MessageGroupId, Group> _groups;
std::map<MessageGroupId, MessageGroupId> _alias;
};
} // namespace Data

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,382 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class HistoryItem;
class HistoryMedia;
namespace base {
template <typename Enum>
class enum_mask;
} // namespace base
namespace Storage {
enum class SharedMediaType : char;
using SharedMediaTypesMask = base::enum_mask<SharedMediaType>;
} // namespace Storage
namespace HistoryView {
enum class Context : char;
class Element;
} // namespace HistoryView
namespace Data {
enum class CallFinishReason : char {
Missed,
Busy,
Disconnected,
Hangup,
};
struct SharedContact {
UserId userId = 0;
QString firstName;
QString lastName;
QString phoneNumber;
};
struct Call {
using FinishReason = CallFinishReason;
int duration = 0;
FinishReason finishReason = FinishReason::Missed;
};
struct Invoice {
MsgId receiptMsgId = 0;
uint64 amount = 0;
QString currency;
QString title;
QString description;
PhotoData *photo = nullptr;
bool isTest = false;
};
class Media {
public:
Media(not_null<HistoryItem*> parent);
virtual ~Media() = default;
not_null<HistoryItem*> parent() const;
virtual std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) = 0;
virtual DocumentData *document() const;
virtual PhotoData *photo() const;
virtual WebPageData *webpage() const;
virtual const SharedContact *sharedContact() const;
virtual const Call *call() const;
virtual GameData *game() const;
virtual const Invoice *invoice() const;
virtual LocationData *location() const;
virtual bool uploading() const;
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const;
virtual bool canBeGrouped() const;
virtual bool hasReplyPreview() const;
virtual ImagePtr replyPreview() const;
// Returns text with link-start and link-end commands for service-color highlighting.
// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
virtual QString chatsListText() const;
virtual QString notificationText() const = 0;
virtual QString pinnedTextSubstring() const = 0;
virtual TextWithEntities clipboardText() const = 0;
virtual bool allowsForward() const;
virtual bool allowsEdit() const;
virtual bool allowsEditCaption() const;
virtual bool allowsRevoke() const;
virtual bool forwardedBecomesUnread() const;
virtual QString errorTextForForward(
not_null<ChannelData*> channel) const;
[[nodiscard]] virtual bool consumeMessageText(
const TextWithEntities &text);
// After sending an inline result we may want to completely recreate
// the media (all media that was generated on client side, for example).
virtual bool updateInlineResultMedia(const MTPMessageMedia &media) = 0;
virtual bool updateSentMedia(const MTPMessageMedia &media) = 0;
virtual std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message,
not_null<HistoryItem*> realParent) = 0;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message);
private:
const not_null<HistoryItem*> _parent;
};
class MediaPhoto : public Media {
public:
MediaPhoto(
not_null<HistoryItem*> parent,
not_null<PhotoData*> photo);
MediaPhoto(
not_null<HistoryItem*> parent,
not_null<PeerData*> chat,
not_null<PhotoData*> photo);
~MediaPhoto();
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
PhotoData *photo() const override;
bool uploading() const override;
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
bool canBeGrouped() const override;
bool hasReplyPreview() const override;
ImagePtr replyPreview() const override;
QString chatsListText() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextWithEntities clipboardText() const override;
bool allowsEditCaption() const override;
QString errorTextForForward(
not_null<ChannelData*> channel) const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message,
not_null<HistoryItem*> realParent) override;
private:
not_null<PhotoData*> _photo;
PeerData *_chat = nullptr;
};
class MediaFile : public Media {
public:
MediaFile(
not_null<HistoryItem*> parent,
not_null<DocumentData*> document);
~MediaFile();
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
DocumentData *document() const override;
bool uploading() const override;
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
bool canBeGrouped() const override;
bool hasReplyPreview() const override;
ImagePtr replyPreview() const override;
QString chatsListText() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextWithEntities clipboardText() const override;
bool allowsEditCaption() const override;
bool forwardedBecomesUnread() const override;
QString errorTextForForward(
not_null<ChannelData*> channel) const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message,
not_null<HistoryItem*> realParent) override;
private:
not_null<DocumentData*> _document;
QString _emoji;
};
class MediaContact : public Media {
public:
MediaContact(
not_null<HistoryItem*> parent,
UserId userId,
const QString &firstName,
const QString &lastName,
const QString &phoneNumber);
~MediaContact();
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
const SharedContact *sharedContact() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextWithEntities clipboardText() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message,
not_null<HistoryItem*> realParent) override;
private:
SharedContact _contact;
};
class MediaLocation : public Media {
public:
MediaLocation(
not_null<HistoryItem*> parent,
const LocationCoords &coords);
MediaLocation(
not_null<HistoryItem*> parent,
const LocationCoords &coords,
const QString &title,
const QString &description);
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
LocationData *location() const override;
QString chatsListText() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextWithEntities clipboardText() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message,
not_null<HistoryItem*> realParent) override;
private:
not_null<LocationData*> _location;
QString _title;
QString _description;
};
class MediaCall : public Media {
public:
MediaCall(
not_null<HistoryItem*> parent,
const MTPDmessageActionPhoneCall &call);
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
const Call *call() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextWithEntities clipboardText() const override;
bool allowsForward() const override;
bool allowsRevoke() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message,
not_null<HistoryItem*> realParent) override;
static QString Text(
not_null<HistoryItem*> item,
CallFinishReason reason);
private:
Call _call;
};
class MediaWebPage : public Media {
public:
MediaWebPage(
not_null<HistoryItem*> parent,
not_null<WebPageData*> page);
~MediaWebPage();
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
WebPageData *webpage() const override;
bool hasReplyPreview() const override;
ImagePtr replyPreview() const override;
QString chatsListText() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextWithEntities clipboardText() const override;
bool allowsEdit() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message,
not_null<HistoryItem*> realParent) override;
private:
not_null<WebPageData*> _page;
};
class MediaGame : public Media {
public:
MediaGame(
not_null<HistoryItem*> parent,
not_null<GameData*> game);
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
GameData *game() const override;
bool hasReplyPreview() const override;
ImagePtr replyPreview() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextWithEntities clipboardText() const override;
QString errorTextForForward(
not_null<ChannelData*> channel) const override;
bool consumeMessageText(const TextWithEntities &text) override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message,
not_null<HistoryItem*> realParent) override;
private:
not_null<GameData*> _game;
TextWithEntities _consumedText;
};
class MediaInvoice : public Media {
public:
MediaInvoice(
not_null<HistoryItem*> parent,
const MTPDmessageMediaInvoice &data);
MediaInvoice(
not_null<HistoryItem*> parent,
const Invoice &data);
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
const Invoice *invoice() const override;
bool hasReplyPreview() const override;
ImagePtr replyPreview() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextWithEntities clipboardText() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryMedia> createView(
not_null<HistoryView::Element*> message,
not_null<HistoryItem*> realParent) override;
private:
Invoice _invoice;
};
TextWithEntities WithCaptionClipboardText(
const QString &attachType,
TextWithEntities &&caption);
} // namespace Data

View File

@@ -0,0 +1,491 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_feed_messages.h"
namespace Data {
MessagesList::Slice::Slice(
base::flat_set<MessagePosition> &&messages,
MessagesRange range)
: messages(std::move(messages))
, range(range) {
}
template <typename Range>
void MessagesList::Slice::merge(
const Range &moreMessages,
MessagesRange moreNoSkipRange) {
Expects(moreNoSkipRange.from <= range.till);
Expects(range.from <= moreNoSkipRange.till);
messages.merge(std::begin(moreMessages), std::end(moreMessages));
range = {
qMin(range.from, moreNoSkipRange.from),
qMax(range.till, moreNoSkipRange.till)
};
}
template <typename Range>
int MessagesList::uniteAndAdd(
MessagesSliceUpdate &update,
base::flat_set<Slice>::iterator uniteFrom,
base::flat_set<Slice>::iterator uniteTill,
const Range &messages,
MessagesRange noSkipRange) {
auto uniteFromIndex = uniteFrom - _slices.begin();
auto was = uniteFrom->messages.size();
_slices.modify(uniteFrom, [&](Slice &slice) {
slice.merge(messages, noSkipRange);
});
auto firstToErase = uniteFrom + 1;
if (firstToErase != uniteTill) {
for (auto it = firstToErase; it != uniteTill; ++it) {
_slices.modify(uniteFrom, [&](Slice &slice) {
slice.merge(it->messages, it->range);
});
}
_slices.erase(firstToErase, uniteTill);
uniteFrom = _slices.begin() + uniteFromIndex;
}
update.messages = &uniteFrom->messages;
update.range = uniteFrom->range;
return uniteFrom->messages.size() - was;
}
template <typename Range>
int MessagesList::addRangeItemsAndCountNew(
MessagesSliceUpdate &update,
const Range &messages,
MessagesRange noSkipRange) {
Expects(noSkipRange.from <= noSkipRange.till);
auto uniteFrom = ranges::lower_bound(
_slices,
noSkipRange.from,
std::less<>(),
[](const Slice &slice) { return slice.range.till; });
auto uniteTill = ranges::upper_bound(
_slices,
noSkipRange.till,
std::less<>(),
[](const Slice &slice) { return slice.range.from; });
if (uniteFrom < uniteTill) {
return uniteAndAdd(update, uniteFrom, uniteTill, messages, noSkipRange);
}
auto sliceMessages = base::flat_set<MessagePosition> {
std::begin(messages),
std::end(messages) };
auto slice = _slices.emplace(
std::move(sliceMessages),
noSkipRange);
update.messages = &slice->messages;
update.range = slice->range;
return slice->messages.size();
}
template <typename Range>
void MessagesList::addRange(
const Range &messages,
MessagesRange noSkipRange,
base::optional<int> count,
bool incrementCount) {
Expects(!count || !incrementCount);
auto wasCount = _count;
auto update = MessagesSliceUpdate();
auto result = addRangeItemsAndCountNew(
update,
messages,
noSkipRange);
if (count) {
_count = count;
} else if (incrementCount && _count && result > 0) {
*_count += result;
}
if (_slices.size() == 1) {
if (_slices.front().range == FullMessagesRange) {
_count = _slices.front().messages.size();
}
}
update.count = _count;
_sliceUpdated.fire(std::move(update));
}
void MessagesList::addNew(MessagePosition messageId) {
auto range = { messageId };
addRange(range, { messageId, MaxMessagePosition }, base::none, true);
}
void MessagesList::addSlice(
std::vector<MessagePosition> &&messageIds,
MessagesRange noSkipRange,
base::optional<int> count) {
addRange(messageIds, noSkipRange, count);
}
void MessagesList::removeOne(MessagePosition messageId) {
auto slice = ranges::lower_bound(
_slices,
messageId,
std::less<>(),
[](const Slice &slice) { return slice.range.till; });
if (slice != _slices.end() && slice->range.from <= messageId) {
_slices.modify(slice, [&](Slice &slice) {
return slice.messages.remove(messageId);
});
}
if (_count) {
--*_count;
}
}
void MessagesList::removeAll(ChannelId channelId) {
auto removed = 0;
for (auto i = begin(_slices); i != end(_slices); ++i) {
_slices.modify(i, [&](Slice &slice) {
auto &messages = slice.messages;
for (auto j = begin(messages); j != end(messages);) {
if (j->fullId.channel == channelId) {
j = messages.erase(j);
++removed;
} else {
++j;
}
}
});
}
if (removed && _count) {
*_count -= removed;
}
}
void MessagesList::invalidate() {
_slices.clear();
_count = base::none;
}
void MessagesList::invalidateBottom() {
if (!_slices.empty()) {
const auto &last = _slices.back();
if (last.range.till == MaxMessagePosition) {
_slices.modify(_slices.end() - 1, [](Slice &slice) {
slice.range.till = slice.messages.empty()
? slice.range.from
: slice.messages.back();
});
}
}
_count = base::none;
}
rpl::producer<MessagesResult> MessagesList::query(
MessagesQuery &&query) const {
return [this, query = std::move(query)](auto consumer) {
auto slice = query.aroundId
? ranges::lower_bound(
_slices,
query.aroundId,
std::less<>(),
[](const Slice &slice) { return slice.range.till; })
: _slices.end();
if (slice != _slices.end()
&& slice->range.from <= query.aroundId) {
consumer.put_next(queryFromSlice(query, *slice));
}
consumer.put_done();
return rpl::lifetime();
};
}
rpl::producer<MessagesSliceUpdate> MessagesList::sliceUpdated() const {
return _sliceUpdated.events();
}
MessagesResult MessagesList::queryFromSlice(
const MessagesQuery &query,
const Slice &slice) const {
auto result = MessagesResult {};
auto position = ranges::lower_bound(slice.messages, query.aroundId);
auto haveBefore = int(position - begin(slice.messages));
auto haveEqualOrAfter = int(end(slice.messages) - position);
auto before = qMin(haveBefore, query.limitBefore);
auto equalOrAfter = qMin(haveEqualOrAfter, query.limitAfter + 1);
auto ids = std::vector<MessagePosition>(position - before, position + equalOrAfter);
result.messageIds.merge(ids.begin(), ids.end());
if (slice.range.from == MinMessagePosition) {
result.skippedBefore = haveBefore - before;
}
if (slice.range.till == MaxMessagePosition) {
result.skippedAfter = haveEqualOrAfter - equalOrAfter;
}
if (_count) {
result.count = _count;
if (!result.skippedBefore && result.skippedAfter) {
result.skippedBefore = *result.count
- *result.skippedAfter
- int(result.messageIds.size());
} else if (!result.skippedAfter && result.skippedBefore) {
result.skippedAfter = *result.count
- *result.skippedBefore
- int(result.messageIds.size());
}
}
return result;
}
MessagesSliceBuilder::MessagesSliceBuilder(
Key key,
int limitBefore,
int limitAfter)
: _key(key)
, _limitBefore(limitBefore)
, _limitAfter(limitAfter) {
}
bool MessagesSliceBuilder::applyInitial(const MessagesResult &result) {
mergeSliceData(
result.count,
result.messageIds,
result.skippedBefore,
result.skippedAfter);
return true;
}
bool MessagesSliceBuilder::applyUpdate(const MessagesSliceUpdate &update) {
auto intersects = [](MessagesRange range1, MessagesRange range2) {
return (range1.from <= range2.till)
&& (range2.from <= range1.till);
};
auto needMergeMessages = (update.messages != nullptr)
&& intersects(update.range, {
_ids.empty() ? _key : _ids.front(),
_ids.empty() ? _key : _ids.back()
});
if (!needMergeMessages && !update.count) {
return false;
}
auto skippedBefore = (update.range.from == MinMessagePosition)
? 0
: base::optional<int> {};
auto skippedAfter = (update.range.till == MaxMessagePosition)
? 0
: base::optional<int> {};
mergeSliceData(
update.count,
needMergeMessages
? *update.messages
: base::flat_set<MessagePosition> {},
skippedBefore,
skippedAfter);
return true;
}
bool MessagesSliceBuilder::removeOne(MessagePosition messageId) {
auto changed = false;
if (_fullCount && *_fullCount > 0) {
--*_fullCount;
changed = true;
}
if (_ids.contains(messageId)) {
_ids.remove(messageId);
changed = true;
} else if (!_ids.empty()) {
if (_ids.front() > messageId
&& _skippedBefore
&& *_skippedBefore > 0) {
--*_skippedBefore;
changed = true;
} else if (_ids.back() < messageId
&& _skippedAfter
&& *_skippedAfter > 0) {
--*_skippedAfter;
changed = true;
}
}
return changed;
}
bool MessagesSliceBuilder::removeAll() {
_ids = {};
_range = FullMessagesRange;
_fullCount = 0;
_skippedBefore = 0;
_skippedAfter = 0;
return true;
}
bool MessagesSliceBuilder::removeFromChannel(ChannelId channelId) {
for (auto i = _ids.begin(); i != _ids.end();) {
if ((*i).fullId.channel == channelId) {
i = _ids.erase(i);
if (_fullCount) {
--*_fullCount;
}
} else {
++i;
}
}
_skippedBefore = _skippedAfter = base::none;
checkInsufficient();
return true;
}
bool MessagesSliceBuilder::invalidated() {
_fullCount = _skippedBefore = _skippedAfter = base::none;
_ids.clear();
checkInsufficient();
return false;
}
bool MessagesSliceBuilder::bottomInvalidated() {
_fullCount = _skippedAfter = base::none;
checkInsufficient();
return true;
}
void MessagesSliceBuilder::checkInsufficient() {
sliceToLimits();
}
void MessagesSliceBuilder::mergeSliceData(
base::optional<int> count,
const base::flat_set<MessagePosition> &messageIds,
base::optional<int> skippedBefore,
base::optional<int> skippedAfter) {
if (messageIds.empty()) {
if (count && _fullCount != count) {
_fullCount = count;
if (*_fullCount <= _ids.size()) {
_fullCount = _ids.size();
_skippedBefore = _skippedAfter = 0;
}
}
fillSkippedAndSliceToLimits();
return;
}
if (count) {
_fullCount = count;
}
const auto impossible = MessagePosition(-1, FullMsgId());
auto wasMinId = _ids.empty() ? impossible : _ids.front();
auto wasMaxId = _ids.empty() ? impossible : _ids.back();
_ids.merge(messageIds.begin(), messageIds.end());
auto adjustSkippedBefore = [&](MessagePosition oldId, int oldSkippedBefore) {
auto it = _ids.find(oldId);
Assert(it != _ids.end());
_skippedBefore = oldSkippedBefore - (it - _ids.begin());
accumulate_max(*_skippedBefore, 0);
};
if (skippedBefore) {
adjustSkippedBefore(messageIds.front(), *skippedBefore);
} else if (wasMinId != impossible && _skippedBefore) {
adjustSkippedBefore(wasMinId, *_skippedBefore);
} else {
_skippedBefore = base::none;
}
auto adjustSkippedAfter = [&](MessagePosition oldId, int oldSkippedAfter) {
auto it = _ids.find(oldId);
Assert(it != _ids.end());
_skippedAfter = oldSkippedAfter - (_ids.end() - it - 1);
accumulate_max(*_skippedAfter, 0);
};
if (skippedAfter) {
adjustSkippedAfter(messageIds.back(), *skippedAfter);
} else if (wasMaxId != impossible && _skippedAfter) {
adjustSkippedAfter(wasMaxId, *_skippedAfter);
} else {
_skippedAfter = base::none;
}
fillSkippedAndSliceToLimits();
}
void MessagesSliceBuilder::fillSkippedAndSliceToLimits() {
if (_fullCount) {
if (_skippedBefore && !_skippedAfter) {
_skippedAfter = *_fullCount
- *_skippedBefore
- int(_ids.size());
} else if (_skippedAfter && !_skippedBefore) {
_skippedBefore = *_fullCount
- *_skippedAfter
- int(_ids.size());
}
}
sliceToLimits();
}
void MessagesSliceBuilder::sliceToLimits() {
if (!_key) {
if (!_fullCount) {
requestMessagesCount();
}
return;
}
auto requestedSomething = false;
auto aroundIt = ranges::lower_bound(_ids, _key);
auto removeFromBegin = (aroundIt - _ids.begin() - _limitBefore);
auto removeFromEnd = (_ids.end() - aroundIt - _limitAfter - 1);
if (removeFromBegin > 0) {
_ids.erase(_ids.begin(), _ids.begin() + removeFromBegin);
if (_skippedBefore) {
*_skippedBefore += removeFromBegin;
}
} else if (removeFromBegin < 0
&& (!_skippedBefore || *_skippedBefore > 0)) {
requestedSomething = true;
requestMessages(RequestDirection::Before);
}
if (removeFromEnd > 0) {
_ids.erase(_ids.end() - removeFromEnd, _ids.end());
if (_skippedAfter) {
*_skippedAfter += removeFromEnd;
}
} else if (removeFromEnd < 0
&& (!_skippedAfter || *_skippedAfter > 0)) {
requestedSomething = true;
requestMessages(RequestDirection::After);
}
if (!_fullCount && !requestedSomething) {
requestMessagesCount();
}
}
void MessagesSliceBuilder::requestMessages(RequestDirection direction) {
auto requestAroundData = [&]() -> AroundData {
if (_ids.empty()) {
return { _key, Data::LoadDirection::Around };
} else if (direction == RequestDirection::Before) {
return { _ids.front(), Data::LoadDirection::Before };
}
return { _ids.back(), Data::LoadDirection::After };
};
_insufficientAround.fire(requestAroundData());
}
void MessagesSliceBuilder::requestMessagesCount() {
_insufficientAround.fire({
MessagePosition(),
Data::LoadDirection::Around });
}
MessagesSlice MessagesSliceBuilder::snapshot() const {
auto result = MessagesSlice();
result.ids.reserve(_ids.size());
for (const auto &position : _ids) {
result.ids.push_back(position.fullId);
}
result.skippedBefore = _skippedBefore;
result.skippedAfter = _skippedAfter;
result.fullCount = _fullCount;
return result;
}
} // namespace Data

View File

@@ -0,0 +1,255 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Data {
enum class LoadDirection : char {
Around,
Before,
After,
};
struct MessagePosition {
constexpr MessagePosition() = default;
constexpr MessagePosition(TimeId date, FullMsgId fullId)
: fullId(fullId)
, date(date) {
}
explicit operator bool() const {
return (fullId.msg != 0);
}
inline constexpr bool operator<(const MessagePosition &other) const {
if (date < other.date) {
return true;
} else if (other.date < date) {
return false;
}
return (fullId < other.fullId);
}
inline constexpr bool operator>(const MessagePosition &other) const {
return other < *this;
}
inline constexpr bool operator<=(const MessagePosition &other) const {
return !(other < *this);
}
inline constexpr bool operator>=(const MessagePosition &other) const {
return !(*this < other);
}
inline constexpr bool operator==(const MessagePosition &other) const {
return (date == other.date)
&& (fullId == other.fullId);
}
inline constexpr bool operator!=(const MessagePosition &other) const {
return !(*this == other);
}
FullMsgId fullId;
TimeId date = 0;
};
struct MessagesRange {
constexpr MessagesRange() = default;
constexpr MessagesRange(MessagePosition from, MessagePosition till)
: from(from)
, till(till) {
}
inline constexpr bool operator==(const MessagesRange &other) const {
return (from == other.from)
&& (till == other.till);
}
inline constexpr bool operator!=(const MessagesRange &other) const {
return !(*this == other);
}
MessagePosition from;
MessagePosition till;
};
constexpr auto MinDate = TimeId(0);
constexpr auto MaxDate = std::numeric_limits<TimeId>::max();
constexpr auto MinMessagePosition = MessagePosition(
MinDate,
FullMsgId(NoChannel, 1));
constexpr auto MaxMessagePosition = MessagePosition(
MaxDate,
FullMsgId(NoChannel, ServerMaxMsgId - 1));
constexpr auto FullMessagesRange = MessagesRange(
MinMessagePosition,
MaxMessagePosition);
constexpr auto UnreadMessagePosition = MessagePosition(
MinDate,
FullMsgId(NoChannel, ShowAtUnreadMsgId));
struct MessagesSlice {
std::vector<FullMsgId> ids;
base::optional<int> skippedBefore;
base::optional<int> skippedAfter;
base::optional<int> fullCount;
};
struct MessagesQuery {
MessagesQuery(
MessagePosition aroundId,
int limitBefore,
int limitAfter)
: aroundId(aroundId)
, limitBefore(limitBefore)
, limitAfter(limitAfter) {
}
MessagePosition aroundId;
int limitBefore = 0;
int limitAfter = 0;
};
struct MessagesResult {
base::optional<int> count;
base::optional<int> skippedBefore;
base::optional<int> skippedAfter;
base::flat_set<MessagePosition> messageIds;
};
struct MessagesSliceUpdate {
const base::flat_set<MessagePosition> *messages = nullptr;
MessagesRange range;
base::optional<int> count;
};
class MessagesList {
public:
void addNew(MessagePosition messageId);
void addSlice(
std::vector<MessagePosition> &&messageIds,
MessagesRange noSkipRange,
base::optional<int> count);
void removeOne(MessagePosition messageId);
void removeAll(ChannelId channelId);
void invalidate();
void invalidateBottom();
rpl::producer<MessagesResult> query(MessagesQuery &&query) const;
rpl::producer<MessagesSliceUpdate> sliceUpdated() const;
private:
struct Slice {
Slice(
base::flat_set<MessagePosition> &&messages,
MessagesRange range);
template <typename Range>
void merge(
const Range &moreMessages,
MessagesRange moreNoSkipRange);
base::flat_set<MessagePosition> messages;
MessagesRange range;
inline bool operator<(const Slice &other) const {
return range.from < other.range.from;
}
};
template <typename Range>
int uniteAndAdd(
MessagesSliceUpdate &update,
base::flat_set<Slice>::iterator uniteFrom,
base::flat_set<Slice>::iterator uniteTill,
const Range &messages,
MessagesRange noSkipRange);
template <typename Range>
int addRangeItemsAndCountNew(
MessagesSliceUpdate &update,
const Range &messages,
MessagesRange noSkipRange);
template <typename Range>
void addRange(
const Range &messages,
MessagesRange noSkipRange,
base::optional<int> count,
bool incrementCount = false);
MessagesResult queryFromSlice(
const MessagesQuery &query,
const Slice &slice) const;
base::optional<int> _count;
base::flat_set<Slice> _slices;
rpl::event_stream<MessagesSliceUpdate> _sliceUpdated;
};
class MessagesSliceBuilder {
public:
using Key = MessagePosition;
MessagesSliceBuilder(Key key, int limitBefore, int limitAfter);
bool applyInitial(const MessagesResult &result);
bool applyUpdate(const MessagesSliceUpdate &update);
bool removeOne(MessagePosition messageId);
bool removeFromChannel(ChannelId channelId);
bool removeAll();
bool invalidated();
bool bottomInvalidated();
void checkInsufficient();
struct AroundData {
MessagePosition aroundId;
LoadDirection direction = LoadDirection::Around;
inline bool operator<(const AroundData &other) const {
return (aroundId < other.aroundId)
|| ((aroundId == other.aroundId)
&& (direction < other.direction));
}
};
auto insufficientAround() const {
return _insufficientAround.events();
}
MessagesSlice snapshot() const;
private:
enum class RequestDirection {
Before,
After,
};
void requestMessages(RequestDirection direction);
void requestMessagesCount();
void fillSkippedAndSliceToLimits();
void sliceToLimits();
void mergeSliceData(
base::optional<int> count,
const base::flat_set<MessagePosition> &messageIds,
base::optional<int> skippedBefore = base::none,
base::optional<int> skippedAfter = base::none);
MessagePosition _key;
base::flat_set<MessagePosition> _ids;
MessagesRange _range;
base::optional<int> _fullCount;
base::optional<int> _skippedBefore;
base::optional<int> _skippedAfter;
int _limitBefore = 0;
int _limitAfter = 0;
rpl::event_stream<AroundData> _insufficientAround;
};
} // namespace Data

View File

@@ -12,6 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer_values.h"
#include "data/data_channel_admins.h"
#include "data/data_photo.h"
#include "data/data_feed.h"
#include "data/data_session.h"
#include "history/history.h"
#include "lang/lang_keys.h"
#include "observer_peer.h"
#include "mainwidget.h"
@@ -22,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "messenger.h"
#include "mainwindow.h"
#include "window/window_controller.h"
#include "storage/localstorage.h"
#include "ui/empty_userpic.h"
#include "ui/text_options.h"
@@ -69,7 +73,9 @@ PeerClickHandler::PeerClickHandler(not_null<PeerData*> peer)
void PeerClickHandler::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton && App::wnd()) {
auto controller = App::wnd()->controller();
if (_peer && _peer->isChannel() && controller->historyPeer.current() != _peer) {
if (_peer
&& _peer->isChannel()
&& controller->activeChatCurrent().peer() != _peer) {
if (!_peer->asChannel()->isPublic() && !_peer->asChannel()->amIn()) {
Ui::show(Box<InformBox>(lang(_peer->isMegagroup()
? lng_group_not_accessible
@@ -117,7 +123,7 @@ void PeerData::updateNameDelayed(
Notify::PeerUpdate update(this);
update.flags |= UpdateFlag::NameChanged;
update.oldNameFirstChars = nameFirstChars();
update.oldNameFirstLetters = nameFirstLetters();
if (isUser()) {
if (asUser()->username != newUsername) {
@@ -167,7 +173,7 @@ void PeerData::setUserpic(
void PeerData::setUserpicPhoto(const MTPPhoto &data) {
auto photoId = [&]() -> PhotoId {
if (auto photo = App::feedPhoto(data)) {
if (const auto photo = Auth().data().photo(data)) {
photo->peer = this;
return photo->id;
}
@@ -295,12 +301,19 @@ void PeerData::setUserpicChecked(
|| _userpicLocation != location) {
setUserpic(photoId, location, userpic);
Notify::peerUpdatedDelayed(this, UpdateFlag::PhotoChanged);
if (const auto channel = asChannel()) {
if (const auto feed = channel->feed()) {
Auth().data().notifyFeedUpdated(
feed,
Data::FeedUpdateFlag::ChannelPhoto);
}
}
}
}
void PeerData::fillNames() {
_nameWords.clear();
_nameFirstChars.clear();
_nameFirstLetters.clear();
auto toIndexList = QStringList();
auto appendToIndex = [&](const QString &value) {
if (!value.isEmpty()) {
@@ -314,7 +327,7 @@ void PeerData::fillNames() {
if (appendTranslit) {
appendToIndex(translitRusEng(toIndexList.front()));
}
if (auto user = asUser()) {
if (const auto user = asUser()) {
if (user->nameOrPhone != name) {
appendToIndex(user->nameOrPhone);
}
@@ -322,7 +335,7 @@ void PeerData::fillNames() {
if (isSelf()) {
appendToIndex(lang(lng_saved_messages));
}
} else if (auto channel = asChannel()) {
} else if (const auto channel = asChannel()) {
appendToIndex(channel->username);
}
auto toIndex = toIndexList.join(' ');
@@ -331,7 +344,7 @@ void PeerData::fillNames() {
const auto namesList = TextUtilities::PrepareSearchWords(toIndex);
for (const auto &name : namesList) {
_nameWords.insert(name);
_nameFirstChars.insert(name[0]);
_nameFirstLetters.insert(name[0]);
}
}
@@ -348,7 +361,26 @@ const Text &BotCommand::descriptionText() const {
}
bool UserData::canShareThisContact() const {
return canShareThisContactFast() || !App::phoneFromSharedContact(peerToUser(id)).isEmpty();
return canShareThisContactFast()
|| !Auth().data().findContactPhone(peerToUser(id)).isEmpty();
}
void UserData::setContactStatus(ContactStatus status) {
if (_contactStatus != status) {
const auto changed = (_contactStatus == ContactStatus::Contact)
!= (status == ContactStatus::Contact);
_contactStatus = status;
if (changed) {
Notify::peerUpdatedDelayed(
this,
Notify::PeerUpdate::Flag::UserIsContact);
}
}
if (_contactStatus == ContactStatus::Contact
&& cReportSpamStatuses().value(id, dbiprsHidden) != dbiprsHidden) {
cRefReportSpamStatuses().insert(id, dbiprsHidden);
Local::writeReportSpamStatuses();
}
}
// see Local::readPeer as well
@@ -819,13 +851,43 @@ 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);
}
}
void ChannelData::setFeed(not_null<Data::Feed*> feed) {
setFeedPointer(feed);
}
void ChannelData::clearFeed() {
setFeedPointer(nullptr);
}
void ChannelData::setFeedPointer(Data::Feed *feed) {
if (_feed != feed) {
const auto was = _feed;
_feed = feed;
if (was) {
was->unregisterOne(this);
}
if (_feed) {
_feed->registerOne(this);
}
}
}

View File

@@ -22,6 +22,8 @@ class ChannelData;
namespace Data {
class Feed;
int PeerColorIndex(PeerId peerId);
int PeerColorIndex(int32 bareId);
style::color PeerUserpicColor(PeerId peerId);
@@ -103,6 +105,7 @@ public:
ChatData *migrateFrom() const;
ChannelData *migrateTo() const;
Data::Feed *feed() const;
void updateFull();
void updateFullForced();
@@ -123,13 +126,11 @@ public:
QString name;
Text nameText;
using NameWords = base::flat_set<QString>;
using NameFirstChars = base::flat_set<QChar>;
const NameWords &nameWords() const {
const base::flat_set<QString> &nameWords() const {
return _nameWords;
}
const NameFirstChars &nameFirstChars() const {
return _nameFirstChars;
const base::flat_set<QChar> &nameFirstLetters() const {
return _nameFirstLetters;
}
enum LoadedStatus {
@@ -240,8 +241,8 @@ private:
Data::NotifySettings _notify;
ClickHandlerPtr _openLink;
NameWords _nameWords; // for filtering
NameFirstChars _nameFirstChars;
base::flat_set<QString> _nameWords; // for filtering
base::flat_set<QChar> _nameFirstLetters;
TimeMs _lastFullUpdate = 0;
@@ -384,7 +385,7 @@ public:
return !isInaccessible();
}
bool isContact() const {
return (contact > 0);
return (_contactStatus == ContactStatus::Contact);
}
bool canShareThisContact() const;
@@ -410,9 +411,18 @@ public:
QString nameOrPhone;
Text phoneText;
TimeId onlineTill = 0;
int32 contact = -1; // -1 - not contact, cant add (self, empty, deleted, foreign), 0 - not contact, can add (request), 1 - contact
enum class BlockStatus {
enum class ContactStatus : char {
PhoneUnknown,
CanAdd,
Contact,
};
ContactStatus contactStatus() const {
return _contactStatus;
}
void setContactStatus(ContactStatus status);
enum class BlockStatus : char {
Unknown,
Blocked,
NotBlocked,
@@ -425,7 +435,7 @@ public:
}
void setBlockStatus(BlockStatus blockStatus);
enum class CallsStatus {
enum class CallsStatus : char {
Unknown,
Enabled,
Disabled,
@@ -461,6 +471,7 @@ private:
QString _restrictionReason;
QString _about;
QString _phone;
ContactStatus _contactStatus = ContactStatus::PhoneUnknown;
BlockStatus _blockStatus = BlockStatus::Unknown;
CallsStatus _callsStatus = CallsStatus::Unknown;
int _commonChatsCount = 0;
@@ -935,8 +946,8 @@ public:
|| amCreator();
}
int32 inviter = 0; // > 0 - user who invited me to channel, < 0 - not in channel
QDateTime inviteDate;
UserId inviter = 0; // > 0 - user who invited me to channel, < 0 - not in channel
TimeId inviteDate = 0;
void ptsInit(int32 pts) {
_ptsWaiter.init(pts);
@@ -999,11 +1010,19 @@ public:
setPinnedMessageId(0);
}
void setFeed(not_null<Data::Feed*> feed);
void clearFeed();
Data::Feed *feed() const {
return _feed;
}
private:
void flagsUpdated(MTPDchannel::Flags diff);
void fullFlagsUpdated(MTPDchannelFull::Flags diff);
bool canEditLastAdmin(not_null<UserData*> user) const;
void setFeedPointer(Data::Feed *feed);
Flags _flags = Flags(MTPDchannel_ClientFlag::f_forbidden | 0);
FullFlags _fullFlags;
@@ -1025,6 +1044,7 @@ private:
QString _about;
QString _inviteLink;
Data::Feed *_feed = nullptr;
rpl::lifetime _lifetime;
@@ -1095,16 +1115,26 @@ inline bool isMegagroup(const PeerData *peer) {
return peer ? peer->isMegagroup() : false;
}
inline ChatData *PeerData::migrateFrom() const {
return (isMegagroup() && asChannel()->amIn())
? asChannel()->mgInfo->migrateFromPtr
: nullptr;
if (const auto megagroup = asMegagroup()) {
return megagroup->amIn()
? megagroup->mgInfo->migrateFromPtr
: nullptr;
}
return nullptr;
}
inline ChannelData *PeerData::migrateTo() const {
return (isChat()
&& asChat()->migrateToPtr
&& asChat()->migrateToPtr->amIn())
? asChat()->migrateToPtr
: nullptr;
if (const auto chat = asChat()) {
if (const auto migrateTo = chat->migrateToPtr) {
return migrateTo->amIn() ? migrateTo : nullptr;
}
}
return nullptr;
}
inline Data::Feed *PeerData::feed() const {
if (const auto channel = asChannel()) {
return channel->feed();
}
return nullptr;
}
inline const Text &PeerData::dialogName() const {
return migrateTo()

View File

@@ -14,6 +14,7 @@ namespace {
constexpr auto kMinOnlineChangeTimeout = TimeMs(1000);
constexpr auto kMaxOnlineChangeTimeout = 86400 * TimeMs(1000);
constexpr auto kSecondsInDay = 86400;
int OnlinePhraseChangeInSeconds(TimeId online, TimeId now) {
if (online <= 0) {
@@ -33,7 +34,7 @@ int OnlinePhraseChangeInSeconds(TimeId online, TimeId now) {
if (hours < 12) {
return (hours + 1) * 3600 - (now - online);
}
const auto nowFull = ::date(now);
const auto nowFull = ParseDateTime(now);
const auto tomorrow = QDateTime(nowFull.date().addDays(1));
return static_cast<int32>(nowFull.secsTo(tomorrow));
}
@@ -175,28 +176,21 @@ TimeId SortByOnlineValue(not_null<UserData*> user, TimeId now) {
return -1;
}
const auto online = user->onlineTill;
const auto fromDate = [](const QDate &date) {
const auto shift = (unixtime() - myunixtime());
return static_cast<TimeId>(QDateTime(date).toTime_t()) + shift;
};
if (online <= 0) {
switch (online) {
case 0:
case -1: return online;
case -2: {
const auto recently = date(now).date().addDays(-3);
return fromDate(recently);
return now - 3 * kSecondsInDay;
} break;
case -3: {
const auto weekago = date(now).date().addDays(-7);
return fromDate(weekago);
return now - 7 * kSecondsInDay;
} break;
case -4: {
const auto monthago = date(now).date().addDays(-30);
return fromDate(monthago);
return now - 30 * kSecondsInDay;
} break;
}
return -online;
@@ -234,8 +228,8 @@ QString OnlineText(TimeId online, TimeId now) {
if (hours < 12) {
return lng_status_lastseen_hours(lt_count, hours);
}
const auto onlineFull = ::date(online);
const auto nowFull = ::date(now);
const auto onlineFull = ParseDateTime(online);
const auto nowFull = ParseDateTime(now);
if (onlineFull.date() == nowFull.date()) {
const auto onlineTime = onlineFull.time().toString(cTimeFormat());
return lng_status_lastseen_today(lt_time, onlineTime);
@@ -260,8 +254,8 @@ QString OnlineTextFull(not_null<UserData*> user, TimeId now) {
} else if (const auto common = OnlineTextCommon(user->onlineTill, now)) {
return *common;
}
const auto onlineFull = ::date(user->onlineTill);
const auto nowFull = ::date(now);
const auto onlineFull = ParseDateTime(user->onlineTill);
const auto nowFull = ParseDateTime(now);
if (onlineFull.date() == nowFull.date()) {
const auto onlineTime = onlineFull.time().toString(cTimeFormat());
return lng_status_lastseen_today(lt_time, onlineTime);

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_photo.h"
#include "data/data_session.h"
#include "mainwidget.h"
#include "history/history_media_types.h"
#include "auth_session.h"
@@ -31,14 +32,14 @@ void PhotoData::automaticLoadSettingsChanged() {
void PhotoData::download() {
full->loadEvenCancelled();
notifyLayoutChanged();
Auth().data().notifyPhotoLayoutChanged(this);
}
bool PhotoData::loaded() const {
bool wasLoading = loading();
if (full->loaded()) {
if (wasLoading) {
notifyLayoutChanged();
Auth().data().notifyPhotoLayoutChanged(this);
}
return true;
}
@@ -57,17 +58,7 @@ bool PhotoData::displayLoading() const {
void PhotoData::cancel() {
full->cancel();
notifyLayoutChanged();
}
void PhotoData::notifyLayoutChanged() const {
auto &items = App::photoItems();
auto i = items.constFind(const_cast<PhotoData*>(this));
if (i != items.cend()) {
for_const (auto item, i.value()) {
Auth().data().markItemLayoutChanged(item);
}
}
Auth().data().notifyPhotoLayoutChanged(this);
}
float64 PhotoData::progress() const {
@@ -136,8 +127,7 @@ void PhotoCancelClickHandler::onClickImpl() const {
if (data->uploading()) {
if (const auto item = App::histItemById(context())) {
App::contextItem(item);
App::main()->cancelUploadLayer();
App::main()->cancelUploadLayer(item);
}
} else {
data->cancel();

View File

@@ -49,9 +49,6 @@ public:
std::unique_ptr<Data::UploadState> uploadingData;
private:
void notifyLayoutChanged() const;
};
class PhotoClickHandler : public FileClickHandler {

View File

@@ -8,6 +8,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_search_controller.h"
#include "auth_session.h"
#include "data/data_session.h"
#include "data/data_messages.h"
#include "history/history.h"
#include "history/history_item.h"
namespace Api {
namespace {
@@ -22,7 +26,7 @@ MTPmessages_Search PrepareSearchRequest(
Storage::SharedMediaType type,
const QString &query,
MsgId messageId,
SparseIdsLoadDirection direction) {
Data::LoadDirection direction) {
const auto filter = [&] {
using Type = Storage::SharedMediaType;
switch (type) {
@@ -57,20 +61,21 @@ MTPmessages_Search PrepareSearchRequest(
const auto limit = messageId ? kSharedMediaLimit : 0;
const auto offsetId = [&] {
switch (direction) {
case SparseIdsLoadDirection::Before:
case SparseIdsLoadDirection::Around: return messageId;
case SparseIdsLoadDirection::After: return messageId + 1;
case Data::LoadDirection::Before:
case Data::LoadDirection::Around: return messageId;
case Data::LoadDirection::After: return messageId + 1;
}
Unexpected("Direction in PrepareSearchRequest");
}();
const auto addOffset = [&] {
switch (direction) {
case SparseIdsLoadDirection::Before: return 0;
case SparseIdsLoadDirection::Around: return -limit / 2;
case SparseIdsLoadDirection::After: return -limit;
case Data::LoadDirection::Before: return 0;
case Data::LoadDirection::Around: return -limit / 2;
case Data::LoadDirection::After: return -limit;
}
Unexpected("Direction in PrepareSearchRequest");
}();
const auto hash = int32(0);
return MTPmessages_Search(
MTP_flags(0),
@@ -84,14 +89,15 @@ MTPmessages_Search PrepareSearchRequest(
MTP_int(addOffset),
MTP_int(limit),
MTP_int(maxId),
MTP_int(minId));
MTP_int(minId),
MTP_int(hash));
}
SearchResult ParseSearchResult(
not_null<PeerData*> peer,
Storage::SharedMediaType type,
MsgId messageId,
SparseIdsLoadDirection direction,
Data::LoadDirection direction,
const MTPmessages_Messages &data) {
auto result = SearchResult();
result.noSkipRange = MsgRange{ messageId, messageId };
@@ -157,11 +163,11 @@ SearchResult ParseSearchResult(
if (messageId && result.messageIds.empty()) {
result.noSkipRange = [&]() -> MsgRange {
switch (direction) {
case SparseIdsLoadDirection::Before: // All old loaded.
case Data::LoadDirection::Before: // All old loaded.
return { 0, result.noSkipRange.till };
case SparseIdsLoadDirection::Around: // All loaded.
case Data::LoadDirection::Around: // All loaded.
return { 0, ServerMaxMsgId };
case SparseIdsLoadDirection::After: // All new loaded.
case Data::LoadDirection::After: // All new loaded.
return { result.noSkipRange.from, ServerMaxMsgId };
}
Unexpected("Direction in ParseSearchResult");

View File

@@ -12,6 +12,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_sparse_ids_list.h"
#include "storage/storage_shared_media.h"
#include "base/value_ordering.h"
#include "base/timer.h"
namespace Data {
enum class LoadDirection : char;
} // namespace Data
namespace Api {
@@ -26,13 +31,13 @@ MTPmessages_Search PrepareSearchRequest(
Storage::SharedMediaType type,
const QString &query,
MsgId messageId,
SparseIdsLoadDirection direction);
Data::LoadDirection direction);
SearchResult ParseSearchResult(
not_null<PeerData*> peer,
Storage::SharedMediaType type,
MsgId messageId,
SparseIdsLoadDirection direction,
Data::LoadDirection direction,
const MTPmessages_Messages &data);
class SearchController : private MTP::Sender {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,547 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "chat_helpers/stickers.h"
#include "dialogs/dialogs_key.h"
#include "data/data_groups.h"
class HistoryItem;
namespace HistoryView {
struct Group;
class Element;
} // namespace HistoryView
class AuthSession;
namespace Media {
namespace Clip {
class Reader;
} // namespace Clip
} // namespace Media
namespace Data {
class Feed;
enum class FeedUpdateFlag;
struct FeedUpdate;
class Session final {
public:
using ViewElement = HistoryView::Element;
explicit Session(not_null<AuthSession*> session);
~Session();
AuthSession &session() const {
return *_session;
}
[[nodiscard]] base::Variable<bool> &contactsLoaded() {
return _contactsLoaded;
}
[[nodiscard]] base::Variable<bool> &allChatsLoaded() {
return _allChatsLoaded;
}
[[nodiscard]] base::Observable<void> &moreChatsLoaded() {
return _moreChatsLoaded;
}
struct ItemVisibilityQuery {
not_null<HistoryItem*> item;
not_null<bool*> isVisible;
};
[[nodiscard]] base::Observable<ItemVisibilityQuery> &queryItemVisibility() {
return _queryItemVisibility;
}
struct IdChange {
not_null<HistoryItem*> item;
MsgId oldId = 0;
};
void notifyItemIdChange(IdChange event);
[[nodiscard]] rpl::producer<IdChange> itemIdChanged() const;
void notifyItemLayoutChange(not_null<const HistoryItem*> item);
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemLayoutChanged() const;
void notifyViewLayoutChange(not_null<const ViewElement*> view);
[[nodiscard]] rpl::producer<not_null<const ViewElement*>> viewLayoutChanged() const;
void requestItemRepaint(not_null<const HistoryItem*> item);
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemRepaintRequest() const;
void requestViewRepaint(not_null<const ViewElement*> view);
[[nodiscard]] rpl::producer<not_null<const ViewElement*>> viewRepaintRequest() const;
void requestItemResize(not_null<const HistoryItem*> item);
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemResizeRequest() const;
void requestViewResize(not_null<ViewElement*> view);
[[nodiscard]] rpl::producer<not_null<ViewElement*>> viewResizeRequest() const;
void requestItemViewRefresh(not_null<HistoryItem*> item);
[[nodiscard]] rpl::producer<not_null<HistoryItem*>> itemViewRefreshRequest() const;
void requestAnimationPlayInline(not_null<HistoryItem*> item);
[[nodiscard]] rpl::producer<not_null<HistoryItem*>> animationPlayInlineRequest() const;
void notifyHistoryUnloaded(not_null<const History*> history);
[[nodiscard]] rpl::producer<not_null<const History*>> historyUnloaded() const;
void notifyItemRemoved(not_null<const HistoryItem*> item);
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemRemoved() const;
void notifyHistoryCleared(not_null<const History*> history);
[[nodiscard]] rpl::producer<not_null<const History*>> historyCleared() const;
void notifyHistoryChangeDelayed(not_null<History*> history);
[[nodiscard]] rpl::producer<not_null<History*>> historyChanged() const;
void sendHistoryChangeNotifications();
using MegagroupParticipant = std::tuple<
not_null<ChannelData*>,
not_null<UserData*>>;
void removeMegagroupParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user);
[[nodiscard]] rpl::producer<MegagroupParticipant> megagroupParticipantRemoved() const;
[[nodiscard]] rpl::producer<not_null<UserData*>> megagroupParticipantRemoved(
not_null<ChannelData*> channel) const;
void addNewMegagroupParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user);
[[nodiscard]] rpl::producer<MegagroupParticipant> megagroupParticipantAdded() const;
[[nodiscard]] rpl::producer<not_null<UserData*>> megagroupParticipantAdded(
not_null<ChannelData*> channel) const;
void notifyFeedUpdated(not_null<Feed*> feed, FeedUpdateFlag update);
[[nodiscard]] rpl::producer<FeedUpdate> feedUpdated() const;
void notifyStickersUpdated();
[[nodiscard]] rpl::producer<> stickersUpdated() const;
void notifySavedGifsUpdated();
[[nodiscard]] rpl::producer<> savedGifsUpdated() const;
bool stickersUpdateNeeded(TimeMs now) const {
return stickersUpdateNeeded(_lastStickersUpdate, now);
}
void setLastStickersUpdate(TimeMs update) {
_lastStickersUpdate = update;
}
bool recentStickersUpdateNeeded(TimeMs now) const {
return stickersUpdateNeeded(_lastRecentStickersUpdate, now);
}
void setLastRecentStickersUpdate(TimeMs update) {
_lastRecentStickersUpdate = update;
}
bool favedStickersUpdateNeeded(TimeMs now) const {
return stickersUpdateNeeded(_lastFavedStickersUpdate, now);
}
void setLastFavedStickersUpdate(TimeMs update) {
_lastFavedStickersUpdate = update;
}
bool featuredStickersUpdateNeeded(TimeMs now) const {
return stickersUpdateNeeded(_lastFeaturedStickersUpdate, now);
}
void setLastFeaturedStickersUpdate(TimeMs update) {
_lastFeaturedStickersUpdate = update;
}
bool savedGifsUpdateNeeded(TimeMs now) const {
return stickersUpdateNeeded(_lastSavedGifsUpdate, now);
}
void setLastSavedGifsUpdate(TimeMs update) {
_lastSavedGifsUpdate = update;
}
int featuredStickerSetsUnreadCount() const {
return _featuredStickerSetsUnreadCount.current();
}
void setFeaturedStickerSetsUnreadCount(int count) {
_featuredStickerSetsUnreadCount = count;
}
[[nodiscard]] rpl::producer<int> featuredStickerSetsUnreadCountValue() const {
return _featuredStickerSetsUnreadCount.value();
}
const Stickers::Sets &stickerSets() const {
return _stickerSets;
}
Stickers::Sets &stickerSetsRef() {
return _stickerSets;
}
const Stickers::Order &stickerSetsOrder() const {
return _stickerSetsOrder;
}
Stickers::Order &stickerSetsOrderRef() {
return _stickerSetsOrder;
}
const Stickers::Order &featuredStickerSetsOrder() const {
return _featuredStickerSetsOrder;
}
Stickers::Order &featuredStickerSetsOrderRef() {
return _featuredStickerSetsOrder;
}
const Stickers::Order &archivedStickerSetsOrder() const {
return _archivedStickerSetsOrder;
}
Stickers::Order &archivedStickerSetsOrderRef() {
return _archivedStickerSetsOrder;
}
const Stickers::SavedGifs &savedGifs() const {
return _savedGifs;
}
Stickers::SavedGifs &savedGifsRef() {
return _savedGifs;
}
HistoryItemsList idsToItems(const MessageIdsList &ids) const;
MessageIdsList itemsToIds(const HistoryItemsList &items) const;
MessageIdsList itemOrItsGroup(not_null<HistoryItem*> item) const;
int pinnedDialogsCount() const;
const std::deque<Dialogs::Key> &pinnedDialogsOrder() const;
void setPinnedDialog(const Dialogs::Key &key, bool pinned);
void applyPinnedDialogs(const QVector<MTPDialog> &list);
void applyPinnedDialogs(const QVector<MTPDialogPeer> &list);
void reorderTwoPinnedDialogs(
const Dialogs::Key &key1,
const Dialogs::Key &key2);
void photoLoadSettingsChanged();
void voiceLoadSettingsChanged();
void animationLoadSettingsChanged();
void notifyPhotoLayoutChanged(not_null<const PhotoData*> photo);
void notifyDocumentLayoutChanged(
not_null<const DocumentData*> document);
void requestDocumentViewRepaint(not_null<const DocumentData*> document);
void markMediaRead(not_null<const DocumentData*> document);
not_null<PhotoData*> photo(PhotoId id);
not_null<PhotoData*> photo(const MTPPhoto &data);
not_null<PhotoData*> photo(const MTPDphoto &data);
not_null<PhotoData*> photo(
const MTPPhoto &data,
const PreparedPhotoThumbs &thumbs);
not_null<PhotoData*> photo(
PhotoId id,
const uint64 &access,
TimeId date,
const ImagePtr &thumb,
const ImagePtr &medium,
const ImagePtr &full);
void photoConvert(
not_null<PhotoData*> original,
const MTPPhoto &data);
PhotoData *photoFromWeb(const MTPWebDocument &data, ImagePtr thumb);
not_null<DocumentData*> document(DocumentId id);
not_null<DocumentData*> document(const MTPDocument &data);
not_null<DocumentData*> document(const MTPDdocument &data);
not_null<DocumentData*> document(
const MTPdocument &data,
const QPixmap &thumb);
not_null<DocumentData*> document(
DocumentId id,
const uint64 &access,
int32 version,
TimeId date,
const QVector<MTPDocumentAttribute> &attributes,
const QString &mime,
const ImagePtr &thumb,
int32 dc,
int32 size,
const StorageImageLocation &thumbLocation);
void documentConvert(
not_null<DocumentData*> original,
const MTPDocument &data);
DocumentData *documentFromWeb(
const MTPWebDocument &data,
ImagePtr thumb);
not_null<WebPageData*> webpage(WebPageId id);
not_null<WebPageData*> webpage(const MTPWebPage &data);
not_null<WebPageData*> webpage(const MTPDwebPage &data);
not_null<WebPageData*> webpage(const MTPDwebPagePending &data);
not_null<WebPageData*> webpage(
WebPageId id,
const QString &siteName,
const TextWithEntities &content);
not_null<WebPageData*> webpage(
WebPageId id,
const QString &type,
const QString &url,
const QString &displayUrl,
const QString &siteName,
const QString &title,
const TextWithEntities &description,
PhotoData *photo,
DocumentData *document,
int duration,
const QString &author,
TimeId pendingTill);
not_null<GameData*> game(GameId id);
not_null<GameData*> game(const MTPDgame &data);
not_null<GameData*> game(
GameId id,
const uint64 &accessHash,
const QString &shortName,
const QString &title,
const QString &description,
PhotoData *photo,
DocumentData *document);
void gameConvert(
not_null<GameData*> original,
const MTPGame &data);
void registerPhotoItem(
not_null<const PhotoData*> photo,
not_null<HistoryItem*> item);
void unregisterPhotoItem(
not_null<const PhotoData*> photo,
not_null<HistoryItem*> item);
void registerDocumentItem(
not_null<const DocumentData*> document,
not_null<HistoryItem*> item);
void unregisterDocumentItem(
not_null<const DocumentData*> document,
not_null<HistoryItem*> item);
void registerWebPageView(
not_null<const WebPageData*> page,
not_null<ViewElement*> view);
void unregisterWebPageView(
not_null<const WebPageData*> page,
not_null<ViewElement*> view);
void registerWebPageItem(
not_null<const WebPageData*> page,
not_null<HistoryItem*> item);
void unregisterWebPageItem(
not_null<const WebPageData*> page,
not_null<HistoryItem*> item);
void registerGameView(
not_null<const GameData*> game,
not_null<ViewElement*> view);
void unregisterGameView(
not_null<const GameData*> game,
not_null<ViewElement*> view);
void registerContactView(
UserId contactId,
not_null<ViewElement*> view);
void unregisterContactView(
UserId contactId,
not_null<ViewElement*> view);
void registerContactItem(
UserId contactId,
not_null<HistoryItem*> item);
void unregisterContactItem(
UserId contactId,
not_null<HistoryItem*> item);
void registerAutoplayAnimation(
not_null<::Media::Clip::Reader*> reader,
not_null<ViewElement*> view);
void unregisterAutoplayAnimation(
not_null<::Media::Clip::Reader*> reader);
HistoryItem *findWebPageItem(not_null<WebPageData*> page) const;
QString findContactPhone(not_null<UserData*> contact) const;
QString findContactPhone(UserId contactId) const;
void notifyWebPageUpdateDelayed(not_null<WebPageData*> page);
void notifyGameUpdateDelayed(not_null<GameData*> game);
void sendWebPageGameNotifications();
void stopAutoplayAnimations();
void registerItemView(not_null<ViewElement*> view);
void unregisterItemView(not_null<ViewElement*> view);
not_null<Feed*> feed(FeedId id);
Feed *feedLoaded(FeedId id);
void setDefaultFeedId(FeedId id);
FeedId defaultFeedId() const;
rpl::producer<FeedId> defaultFeedIdValue() const;
void forgetMedia();
void setMimeForwardIds(MessageIdsList &&list);
MessageIdsList takeMimeForwardIds();
Groups &groups() {
return _groups;
}
const Groups &groups() const {
return _groups;
}
private:
void setupContactViewsViewer();
void setupChannelLeavingViewer();
void photoApplyFields(
not_null<PhotoData*> photo,
const MTPPhoto &data);
void photoApplyFields(
not_null<PhotoData*> photo,
const MTPDphoto &data);
void photoApplyFields(
not_null<PhotoData*> photo,
const uint64 &access,
TimeId date,
const ImagePtr &thumb,
const ImagePtr &medium,
const ImagePtr &full);
void documentApplyFields(
not_null<DocumentData*> document,
const MTPDocument &data);
void documentApplyFields(
not_null<DocumentData*> document,
const MTPDdocument &data);
void documentApplyFields(
not_null<DocumentData*> document,
const uint64 &access,
int32 version,
TimeId date,
const QVector<MTPDocumentAttribute> &attributes,
const QString &mime,
const ImagePtr &thumb,
int32 dc,
int32 size,
const StorageImageLocation &thumbLocation);
DocumentData *documentFromWeb(
const MTPDwebDocument &data,
ImagePtr thumb);
DocumentData *documentFromWeb(
const MTPDwebDocumentNoProxy &data,
ImagePtr thumb);
void webpageApplyFields(
not_null<WebPageData*> page,
const MTPDwebPage &data);
void webpageApplyFields(
not_null<WebPageData*> page,
const QString &type,
const QString &url,
const QString &displayUrl,
const QString &siteName,
const QString &title,
const TextWithEntities &description,
PhotoData *photo,
DocumentData *document,
int duration,
const QString &author,
TimeId pendingTill);
void gameApplyFields(
not_null<GameData*> game,
const MTPDgame &data);
void gameApplyFields(
not_null<GameData*> game,
const uint64 &accessHash,
const QString &shortName,
const QString &title,
const QString &description,
PhotoData *photo,
DocumentData *document);
bool stickersUpdateNeeded(TimeMs lastUpdate, TimeMs now) const {
constexpr auto kStickersUpdateTimeout = TimeMs(3600'000);
return (lastUpdate == 0)
|| (now >= lastUpdate + kStickersUpdateTimeout);
}
void userIsContactUpdated(not_null<UserData*> user);
void clearPinnedDialogs();
void setIsPinned(const Dialogs::Key &key, bool pinned);
template <typename Method>
void enumerateItemViews(
not_null<const HistoryItem*> item,
Method method);
not_null<AuthSession*> _session;
base::Variable<bool> _contactsLoaded = { false };
base::Variable<bool> _allChatsLoaded = { false };
base::Observable<void> _moreChatsLoaded;
base::Observable<ItemVisibilityQuery> _queryItemVisibility;
rpl::event_stream<IdChange> _itemIdChanges;
rpl::event_stream<not_null<const HistoryItem*>> _itemLayoutChanges;
rpl::event_stream<not_null<const ViewElement*>> _viewLayoutChanges;
rpl::event_stream<not_null<const HistoryItem*>> _itemRepaintRequest;
rpl::event_stream<not_null<const ViewElement*>> _viewRepaintRequest;
rpl::event_stream<not_null<const HistoryItem*>> _itemResizeRequest;
rpl::event_stream<not_null<ViewElement*>> _viewResizeRequest;
rpl::event_stream<not_null<HistoryItem*>> _itemViewRefreshRequest;
rpl::event_stream<not_null<HistoryItem*>> _animationPlayInlineRequest;
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
rpl::event_stream<not_null<const History*>> _historyUnloaded;
rpl::event_stream<not_null<const History*>> _historyCleared;
base::flat_set<not_null<History*>> _historiesChanged;
rpl::event_stream<not_null<History*>> _historyChanged;
rpl::event_stream<MegagroupParticipant> _megagroupParticipantRemoved;
rpl::event_stream<MegagroupParticipant> _megagroupParticipantAdded;
rpl::event_stream<FeedUpdate> _feedUpdates;
rpl::event_stream<> _stickersUpdated;
rpl::event_stream<> _savedGifsUpdated;
TimeMs _lastStickersUpdate = 0;
TimeMs _lastRecentStickersUpdate = 0;
TimeMs _lastFavedStickersUpdate = 0;
TimeMs _lastFeaturedStickersUpdate = 0;
TimeMs _lastSavedGifsUpdate = 0;
rpl::variable<int> _featuredStickerSetsUnreadCount = 0;
Stickers::Sets _stickerSets;
Stickers::Order _stickerSetsOrder;
Stickers::Order _featuredStickerSetsOrder;
Stickers::Order _archivedStickerSetsOrder;
Stickers::SavedGifs _savedGifs;
std::unordered_map<
PhotoId,
std::unique_ptr<PhotoData>> _photos;
std::map<
not_null<const PhotoData*>,
base::flat_set<not_null<HistoryItem*>>> _photoItems;
std::unordered_map<
DocumentId,
std::unique_ptr<DocumentData>> _documents;
std::map<
not_null<const DocumentData*>,
base::flat_set<not_null<HistoryItem*>>> _documentItems;
std::unordered_map<
WebPageId,
std::unique_ptr<WebPageData>> _webpages;
std::map<
not_null<const WebPageData*>,
base::flat_set<not_null<HistoryItem*>>> _webpageItems;
std::map<
not_null<const WebPageData*>,
base::flat_set<not_null<ViewElement*>>> _webpageViews;
std::unordered_map<
GameId,
std::unique_ptr<GameData>> _games;
std::map<
not_null<const GameData*>,
base::flat_set<not_null<ViewElement*>>> _gameViews;
std::map<
UserId,
base::flat_set<not_null<HistoryItem*>>> _contactItems;
std::map<
UserId,
base::flat_set<not_null<ViewElement*>>> _contactViews;
base::flat_map<
not_null<::Media::Clip::Reader*>,
not_null<ViewElement*>> _autoplayAnimations;
base::flat_set<not_null<WebPageData*>> _webpagesUpdated;
base::flat_set<not_null<GameData*>> _gamesUpdated;
std::deque<Dialogs::Key> _pinnedDialogs;
base::flat_map<FeedId, std::unique_ptr<Feed>> _feeds;
rpl::variable<FeedId> _defaultFeedId = FeedId();
Groups _groups;
std::map<
not_null<const HistoryItem*>,
std::vector<not_null<ViewElement*>>> _views;
MessageIdsList _mimeForwardIds;
rpl::lifetime _lifetime;
};
} // namespace Data

Some files were not shown because too many files have changed in this diff Show More