Compare commits
130 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f98fdeab3f | ||
|
|
90179188b9 | ||
|
|
ccef155f7a | ||
|
|
f0a95032a5 | ||
|
|
c3ff5f2603 | ||
|
|
ee182ea684 | ||
|
|
7f73cc3085 | ||
|
|
dcf70b2847 | ||
|
|
cb5ba7edda | ||
|
|
7940ef24ab | ||
|
|
0f901b3728 | ||
|
|
09aba596ac | ||
|
|
b930ac7bf9 | ||
|
|
7f1bc4635a | ||
|
|
d4253d2025 | ||
|
|
b007fcb537 | ||
|
|
e6dd7d7684 | ||
|
|
128663d95b | ||
|
|
ef8b6d1a3d | ||
|
|
b4baebc230 | ||
|
|
b4581a7bbf | ||
|
|
a285dca39e | ||
|
|
00aa6d5ac3 | ||
|
|
027db285bc | ||
|
|
c3c9ba7e51 | ||
|
|
a1be63f890 | ||
|
|
f066f3f139 | ||
|
|
e17dcbb8eb | ||
|
|
1ae22c8606 | ||
|
|
d5569487a4 | ||
|
|
336e691dbc | ||
|
|
17a4d19beb | ||
|
|
74aa1ad71e | ||
|
|
f8c2f339a0 | ||
|
|
1dd66184a1 | ||
|
|
ddab8c1473 | ||
|
|
49d2c97ceb | ||
|
|
351a423337 | ||
|
|
07528be1e6 | ||
|
|
bc171f625a | ||
|
|
0f775e1e66 | ||
|
|
98fb874b29 | ||
|
|
cfd5c2a650 | ||
|
|
22a5b7faf6 | ||
|
|
fe262701c0 | ||
|
|
e1f71d3919 | ||
|
|
906cb95e67 | ||
|
|
f23c23f696 | ||
|
|
99c686e3e1 | ||
|
|
a144e35f84 | ||
|
|
5a5c5782a9 | ||
|
|
a2a5c30e60 | ||
|
|
3c4c466f8e | ||
|
|
6726826c17 | ||
|
|
e102cb1469 | ||
|
|
11671e85da | ||
|
|
b8614c60f9 | ||
|
|
269defa82d | ||
|
|
8bacc74d8b | ||
|
|
0c5efb935d | ||
|
|
a7f67c4bc9 | ||
|
|
edcaccba1f | ||
|
|
5ebecb4de3 | ||
|
|
9f3048c1dc | ||
|
|
2586268b81 | ||
|
|
280ddb4629 | ||
|
|
20889d7003 | ||
|
|
d4f4698c69 | ||
|
|
adcce61b52 | ||
|
|
17b913fb13 | ||
|
|
366ea1edc3 | ||
|
|
3a5a002be2 | ||
|
|
533fba8c70 | ||
|
|
7435bd7fb0 | ||
|
|
681b9b5ba3 | ||
|
|
600737c44f | ||
|
|
e5f3bed801 | ||
|
|
2fdc3169ce | ||
|
|
722264f634 | ||
|
|
a858ab5d0b | ||
|
|
63c1212ef1 | ||
|
|
2aa477176c | ||
|
|
6bb39451ea | ||
|
|
099a3c4642 | ||
|
|
9515520088 | ||
|
|
fe1a90bd39 | ||
|
|
65df137610 | ||
|
|
ced0c4d8f0 | ||
|
|
f3804429e4 | ||
|
|
a47981054f | ||
|
|
b9ad8bb700 | ||
|
|
47ad5ea98a | ||
|
|
840b42934b | ||
|
|
4527c03c0d | ||
|
|
89941a8e83 | ||
|
|
ebd4651ac2 | ||
|
|
861ab85ca1 | ||
|
|
f9154c4ed0 | ||
|
|
b91ebad8be | ||
|
|
e6baf8ef5b | ||
|
|
d326c7e3fa | ||
|
|
950126865e | ||
|
|
91f369a0b3 | ||
|
|
d1a9d3992b | ||
|
|
2dd2ad5cdb | ||
|
|
04c8c95634 | ||
|
|
8a56ede187 | ||
|
|
7425e80f05 | ||
|
|
97a9089ebf | ||
|
|
bee474f6e9 | ||
|
|
4740d44159 | ||
|
|
062b0b2165 | ||
|
|
8060cb7426 | ||
|
|
794e31505b | ||
|
|
50b120bc22 | ||
|
|
f0b2e445f6 | ||
|
|
782e70b171 | ||
|
|
9d2239291d | ||
|
|
a2891807f8 | ||
|
|
6a9556d42c | ||
|
|
724fe65d72 | ||
|
|
46612ef128 | ||
|
|
139ef5411a | ||
|
|
ac57000437 | ||
|
|
31234cb487 | ||
|
|
05e36a064f | ||
|
|
f88cbf3d4b | ||
|
|
7814ee0f7a | ||
|
|
9f4e5e4603 | ||
|
|
810fb45750 |
107
Telegram/Patches/crashpad.diff
Normal file
107
Telegram/Patches/crashpad.diff
Normal 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:*',
|
||||
@@ -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:*',
|
||||
|
||||
BIN
Telegram/Resources/icons/dialogs_feed.png
Normal file
BIN
Telegram/Resources/icons/dialogs_feed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 328 B |
BIN
Telegram/Resources/icons/dialogs_feed@2x.png
Normal file
BIN
Telegram/Resources/icons/dialogs_feed@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 626 B |
BIN
Telegram/Resources/icons/info_feed.png
Normal file
BIN
Telegram/Resources/icons/info_feed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 748 B |
BIN
Telegram/Resources/icons/info_feed@2x.png
Normal file
BIN
Telegram/Resources/icons/info_feed@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Telegram/Resources/icons/stickers_empty.png
Normal file
BIN
Telegram/Resources/icons/stickers_empty.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/stickers_empty@2x.png
Normal file
BIN
Telegram/Resources/icons/stickers_empty@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -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...";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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()]));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
30
Telegram/SourceFiles/core/event_filter.cpp
Normal file
30
Telegram/SourceFiles/core/event_filter.cpp
Normal 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
|
||||
30
Telegram/SourceFiles/core/event_filter.h
Normal file
30
Telegram/SourceFiles/core/event_filter.h
Normal 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
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
474
Telegram/SourceFiles/data/data_feed.cpp
Normal file
474
Telegram/SourceFiles/data/data_feed.cpp
Normal 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
|
||||
113
Telegram/SourceFiles/data/data_feed.h
Normal file
113
Telegram/SourceFiles/data/data_feed.h
Normal 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
|
||||
103
Telegram/SourceFiles/data/data_feed_messages.cpp
Normal file
103
Telegram/SourceFiles/data/data_feed_messages.cpp
Normal 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
|
||||
24
Telegram/SourceFiles/data/data_feed_messages.h
Normal file
24
Telegram/SourceFiles/data/data_feed_messages.h
Normal 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
|
||||
140
Telegram/SourceFiles/data/data_groups.cpp
Normal file
140
Telegram/SourceFiles/data/data_groups.cpp
Normal 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
|
||||
44
Telegram/SourceFiles/data/data_groups.h
Normal file
44
Telegram/SourceFiles/data/data_groups.h
Normal 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
|
||||
1115
Telegram/SourceFiles/data/data_media_types.cpp
Normal file
1115
Telegram/SourceFiles/data/data_media_types.cpp
Normal file
File diff suppressed because it is too large
Load Diff
382
Telegram/SourceFiles/data/data_media_types.h
Normal file
382
Telegram/SourceFiles/data/data_media_types.h
Normal 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
|
||||
491
Telegram/SourceFiles/data/data_messages.cpp
Normal file
491
Telegram/SourceFiles/data/data_messages.cpp
Normal 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
|
||||
255
Telegram/SourceFiles/data/data_messages.h
Normal file
255
Telegram/SourceFiles/data/data_messages.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -49,9 +49,6 @@ public:
|
||||
|
||||
std::unique_ptr<Data::UploadState> uploadingData;
|
||||
|
||||
private:
|
||||
void notifyLayoutChanged() const;
|
||||
|
||||
};
|
||||
|
||||
class PhotoClickHandler : public FileClickHandler {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
1577
Telegram/SourceFiles/data/data_session.cpp
Normal file
1577
Telegram/SourceFiles/data/data_session.cpp
Normal file
File diff suppressed because it is too large
Load Diff
547
Telegram/SourceFiles/data/data_session.h
Normal file
547
Telegram/SourceFiles/data/data_session.h
Normal 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
Reference in New Issue
Block a user