Compare commits

..

172 Commits

Author SHA1 Message Date
John Preston
3cdd115317 Beta version 2.8.6: Fix build for Linux. 2021-07-07 00:54:56 +03:00
John Preston
bdd1d2484c Beta version 2.8.6: Update build script. 2021-07-06 20:32:08 +03:00
John Preston
d5a416d5ea Beta version 2.8.6: Fix build for macOS. 2021-07-06 20:25:46 +03:00
John Preston
46d393ea0f Beta version 2.8.6.
- Added a simple image editor.
Crop photos or highlight parts of screenshots before sending.
- Use Direct3D 9 backend in ANGLE by default (Windows).
- Fix "Show in Finder" not focusing the Finder window (macOS).
- Use GTK from a child process (Linux).
2021-07-06 17:17:23 +03:00
nyakze
68e351b7c8 Attempt to fix not working commands
Attempt to fix not working commands by using more conventional keys
2021-07-06 17:16:46 +03:00
John Preston
6f9c911faa Fix Qt build instructions with ANGLE. 2021-07-06 17:04:05 +03:00
John Preston
dd381d9b56 Show vector elements count in DebugLogs. 2021-07-06 16:54:26 +03:00
John Preston
8eedc7b2ba Fix build on Windows. 2021-07-06 15:56:43 +03:00
John Preston
9ba1af2eb9 Use tg_angle fork. 2021-07-06 15:55:49 +03:00
23rd
f567328a60 Fixed deleting item after saving result of scene in photo editor. 2021-07-06 14:15:07 +03:00
23rd
953fa52490 Fixed updating of undo state on deleting item in photo editor. 2021-07-06 14:15:07 +03:00
23rd
b1477260f0 Improved content margins in photo editor. 2021-07-06 14:15:07 +03:00
23rd
038de9ef15 Changed behavior to keep StickerSetBox until pack is archived. 2021-07-06 12:13:06 +03:00
23rd
4701c5d6e3 Added ability to archive mask packs. 2021-07-06 12:13:06 +03:00
23rd
8420b7dc17 Disabled ability to use hotkeys when selecting color in photo editor. 2021-07-06 12:13:06 +03:00
23rd
9dacf69d41 Fixed line drawing on mode switching in photo editor. 2021-07-06 12:13:06 +03:00
23rd
a91efd9164 Fixed ability to draw blank lines in photo editor. 2021-07-06 12:13:06 +03:00
23rd
a631a28092 Removed App::pixmapFromImageInPlace. 2021-07-06 12:13:06 +03:00
23rd
7bcb1fc8b2 Fixed borders of drawn lines in photo editor. 2021-07-06 12:13:06 +03:00
23rd
cdafd8f171 Fixed width of preview for small media in SendFilesBox.
Regression was introduced in 42d4fdb89f.
2021-07-06 12:13:06 +03:00
23rd
116aa01e51 Refactored column width in SingleMediaPreview. 2021-07-06 12:13:06 +03:00
23rd
96b40f43e9 Added ability to drag and drop images in photo editor. 2021-07-06 12:13:06 +03:00
23rd
a93ec9c2c2 Added Escape hotkey to clear selection in photo editor. 2021-07-06 12:13:06 +03:00
23rd
3ee3919d50 Added hotkeys for switching between modes in photo editor. 2021-07-06 12:13:06 +03:00
23rd
b4410c49b9 Added background color in photo editor. 2021-07-06 12:13:06 +03:00
23rd
82bf6ca94f Fixed display of sticker panel on mode change in photo editor. 2021-07-06 12:13:06 +03:00
23rd
785ebfee34 Added animation to button bars in photo editor. 2021-07-06 12:13:06 +03:00
23rd
a60f8d75a0 Added second button bar for paint mode in photo editor. 2021-07-06 12:13:06 +03:00
23rd
5976a7ed19 Moved edge buttons to bar in photo editor. 2021-07-06 12:13:06 +03:00
23rd
df7026b59c Made alignment to bottom for button strip in photo editor. 2021-07-06 12:13:06 +03:00
23rd
edfd9bedc1 Improved style for controls in EditCaptionBox. 2021-07-06 12:13:06 +03:00
23rd
f52c6a6daa Improved style for controls in SendFilesBox. 2021-07-06 12:13:06 +03:00
23rd
af10b6d487 Added icons for SendFilesBox. 2021-07-06 12:13:06 +03:00
23rd
e30eacff41 Added photo editor hint to SendFilesBox. 2021-07-06 12:13:06 +03:00
23rd
18154e403a Added ability to open photo editor in SendFilesBox with left-click. 2021-07-06 12:13:06 +03:00
23rd
6975b04e6b Fixed triggering of pressed buttons in album preview. 2021-07-06 12:13:06 +03:00
23rd
948302cf02 Added setting to hide photo editor hint. 2021-07-06 12:13:06 +03:00
23rd
e4cff8cb4b Added photo editor hint to EditCaptionBox. 2021-07-06 12:13:06 +03:00
23rd
5bd17ae1b2 Fixed caption area height in EditCaptionBox.
Regression was introduced in 51f960442e.
2021-07-06 12:13:06 +03:00
23rd
22213a71c1 Added ability to open photo editor in EditCaptionBox with left-click. 2021-07-06 12:13:06 +03:00
23rd
e926e5f882 Slightly improved style of controls in photo editor. 2021-07-06 12:13:06 +03:00
23rd
221d45b500 Updated control icons for photo editor. 2021-07-06 12:13:06 +03:00
23rd
6bb7e2c2eb Removed using of hardcoded numbers of tray menu actions. 2021-07-06 12:13:06 +03:00
23rd
2a86ce596d Added shortcuts for actions of items in photo editor. 2021-07-06 12:13:06 +03:00
23rd
f936e484cc Removed unused types from scene items. 2021-07-06 12:13:05 +03:00
23rd
b2a1c10036 Removed masks panel when there are no masks. 2021-07-06 12:13:05 +03:00
23rd
2a58d01927 Removed search and featured buttons from masks panel. 2021-07-06 12:13:05 +03:00
23rd
7cd6b821b3 Fixed update of recently attached stickers after sending. 2021-07-06 12:13:05 +03:00
23rd
de108c8efe Fixed removing masks set from StickersListWidget. 2021-07-06 12:13:05 +03:00
23rd
e7104b5ebe Added support for archived masks. 2021-07-06 12:13:05 +03:00
23rd
2d17bd02a3 Moved mask management to separate box. 2021-07-06 12:13:05 +03:00
23rd
2a3115f461 Fixed phrases to display mask count. 2021-07-06 12:13:05 +03:00
23rd
8d62800e77 Moved stickerSetInstalled from ApiWrap to Data::Stickers. 2021-07-06 12:13:05 +03:00
23rd
7e04bf9533 Added ability to install mask sets. 2021-07-06 12:13:05 +03:00
23rd
2bd3a8aaff Added ability to delete and reorder mask sets.
Moved ApiWrap::stickerSetDisenabled and ApiWrap::stickersSaveOrder
to ApiWrap::saveStickerSets as lambdas.
2021-07-06 12:13:05 +03:00
23rd
70f92a7817 Added initial masks tab to manage stickers box. 2021-07-06 12:13:05 +03:00
23rd
8e08f69508 Added support updateNewStickerSet and updateStickerSets for masks. 2021-07-06 12:13:05 +03:00
23rd
abe62475cb Added support updateStickerSetsOrder for masks. 2021-07-06 12:13:05 +03:00
23rd
1cdb83462e Added initial implementation of masks panel. 2021-07-06 12:13:05 +03:00
23rd
d9a29b6f15 Fixed item pen width in big images. 2021-07-06 12:13:05 +03:00
23rd
1504f92a64 Fixed size limits of item on big images. 2021-07-06 12:13:05 +03:00
23rd
36e5056b59 Fixed selection of items on mode switching. 2021-07-06 12:13:05 +03:00
23rd
c5c707f0fd Fixed independence of item and scene transforms when adding. 2021-07-06 12:13:05 +03:00
23rd
832dd8d50c Moved some photo editor files to separate directories. 2021-07-06 12:13:05 +03:00
23rd
7d2b20e624 Made TabbedSelector more flexible. 2021-07-06 12:13:05 +03:00
23rd
049945a9b9 Added ability to duplicate items in photo editor. 2021-07-06 12:13:05 +03:00
23rd
808c9e3d2c Added ability to flip items in photo editor. 2021-07-06 12:13:05 +03:00
23rd
fde7cef9c8 Removed using of raw pointers for QGraphicsItem in photo editor.
Now all items are wrapped in the shared_ptr,
and the Scene loses ownership of all items before being destroyed.
2021-07-06 12:13:05 +03:00
23rd
2791f89f30 Added initial context menu to items in photo editor. 2021-07-06 12:13:05 +03:00
23rd
858b5831e8 Fixed clearing of redo list after adding sticker item in photo editor. 2021-07-06 12:13:05 +03:00
23rd
9166423598 Fixed multi selection of items in photo editor. 2021-07-06 12:13:05 +03:00
23rd
184d984336 Added ability to snap rotation of items with Shift key in photo editor. 2021-07-06 12:13:05 +03:00
23rd
0b5044f064 Fixed size of handles of base item in photo editor. 2021-07-06 12:13:05 +03:00
23rd
274b66f74b Added ability to create items in photo editor with different ratios. 2021-07-06 12:13:05 +03:00
23rd
e05343d721 Added sending info of stickered photos. 2021-07-06 12:13:05 +03:00
23rd
bc316a2536 Removed Storage::UploadedThumbDocument struct. 2021-07-06 12:13:05 +03:00
23rd
a6904be81d Slightly optimized mouse painting in photo editor. 2021-07-06 12:13:05 +03:00
23rd
690a7d1608 Fixed undo and redo paint actions. 2021-07-06 12:13:05 +03:00
23rd
a3e54fcd7c Moved draft painting in photo editor to separate files. 2021-07-06 12:13:05 +03:00
23rd
23c67bb2a2 Added ability to add stickers to photo in photo editor. 2021-07-06 12:13:05 +03:00
23rd
75367f0488 Added sticker panel to photo editor. 2021-07-06 12:13:05 +03:00
23rd
216ffad80e Added container of controllers for photo editor. 2021-07-06 12:13:05 +03:00
23rd
c312607ff8 Added stickers panel controller for photo editor. 2021-07-06 12:13:05 +03:00
23rd
812d616f66 Added scene base item for photo editor. 2021-07-06 12:13:05 +03:00
23rd
183408cb2d Added button highlighting for flipped image to photo editor. 2021-07-06 12:13:05 +03:00
23rd
1a7d5b7c95 Removed unused photo crop box. 2021-07-06 12:13:05 +03:00
23rd
17465e1082 Replaced old photo crop box with photo editor for profile photos. 2021-07-06 12:13:05 +03:00
23rd
a996b14291 Fixed keeping of aspect ratio in crop widget. 2021-07-06 12:13:05 +03:00
23rd
2045252cfd Added ability to pass data for photo editor. 2021-07-06 12:13:05 +03:00
23rd
a2e674bdb6 Added Window::Controller pointer to data of intro widget. 2021-07-06 12:13:05 +03:00
23rd
cc4055a5e3 Added method to Window::Controller to show custom layer widget. 2021-07-06 12:13:05 +03:00
23rd
d1b6cf1fae Added draft menu to EditCaptionBox to open photo editor. 2021-07-06 12:13:05 +03:00
23rd
671a06c407 Replaced using of QPixmap in photo editor with Image. 2021-07-06 12:13:05 +03:00
23rd
3ce315111f Added draft menu to SendFilesBox to open photo editor. 2021-07-06 12:13:05 +03:00
23rd
09768ce28a Refactored crop widget in photo editor. 2021-07-06 12:13:05 +03:00
23rd
c9affe0da5 Added custom layer widget with photo editor. 2021-07-06 12:13:05 +03:00
23rd
4909ba5a1e Added ability to pass custom layer widgets to stack. 2021-07-06 12:13:05 +03:00
23rd
e322733e20 Added saving of color and size of brush from photo editor to settings. 2021-07-06 12:13:05 +03:00
23rd
dc7f440902 Added color picker to photo editor. 2021-07-06 12:13:05 +03:00
23rd
4849376347 Added ability to undo and to redo paint actions in photo editor. 2021-07-06 12:13:05 +03:00
23rd
8eca57f419 Added saving and discarding between modes in photo editor. 2021-07-06 12:13:05 +03:00
23rd
0adcd37030 Added edge buttons to controls of photo editor. 2021-07-06 12:13:05 +03:00
23rd
2bdb9af146 Added dummy control icons for photo editor. 2021-07-06 12:13:05 +03:00
23rd
5b6bddd7fc Added initial implementation of mouse drawing in photo editor. 2021-07-06 12:13:05 +03:00
23rd
e1ea833ad6 Added ability to crop images in photo editor. 2021-07-06 12:13:05 +03:00
23rd
85c21ba0e4 Added ability to open photo editor with saved modifications. 2021-07-06 12:13:05 +03:00
23rd
4d72d20398 Added ability to send modified images. 2021-07-06 12:13:04 +03:00
23rd
9d3d16a725 Added initial ability to rotate and flip image to photo editor. 2021-07-06 12:13:04 +03:00
23rd
99deaf6005 Added dummy buttons to PhotoEditorControls. 2021-07-06 12:13:04 +03:00
23rd
d8921c7cf5 Added HorizontalContainer for buttons to PhotoEditorControls. 2021-07-06 12:13:04 +03:00
23rd
f7fa36ca1d Added photo painting to PhotoEditorContent. 2021-07-06 12:13:04 +03:00
23rd
45f8e68203 Initialized empty files of photo editor. 2021-07-06 12:13:04 +03:00
John Preston
2b47d6d63f Add depot_tools information to build instructions. 2021-07-06 12:05:45 +03:00
John Preston
aaefeed3f1 Support custom keyboard placeholders. 2021-07-05 21:12:52 +03:00
John Preston
7f00065bd8 Update API scheme to layer 130. 2021-07-05 21:12:52 +03:00
Ilya Fedin
6f031a715e Revert "Use QMenuBar instead of own global menu implementation on Linux"
This reverts commit 79f96480c2.
2021-07-05 21:01:20 +03:00
John Preston
6981ae605a Fix fake unread status reset on account change. 2021-07-05 19:48:06 +03:00
John Preston
d91c21fb26 Try enabling OpenGL back after switching to ANGLE. 2021-07-05 19:48:06 +03:00
John Preston
c95f052e60 Fix "Show in Finder" app focus bug by a Qt patch. 2021-07-05 19:31:21 +03:00
John Preston
c33be27b3c Update lib_webview submodule. 2021-07-05 15:57:55 +03:00
John Preston
6be9b25e99 Submit voice chat boxes by Enter. 2021-07-05 15:37:34 +03:00
Ilya Fedin
0bb391937e Update lib_webview 2021-07-04 20:05:53 +03:00
Ilya Fedin
75ff7a6637 Control GtkOpenWithDialog lifetime from outside 2021-07-04 20:05:53 +03:00
John Preston
aece7c1096 Remove testing code in stickers view. 2021-07-02 22:11:34 +03:00
John Preston
d2e6e7adf2 Fix build on macOS. 2021-07-02 22:11:23 +03:00
John Preston
b930bc0e6d Track bot commands separately in different chats. 2021-07-02 20:41:48 +03:00
John Preston
93d99d6173 Track only strings in BotCommand struct. 2021-07-02 20:41:46 +03:00
John Preston
a8df3dcf91 Remove test code for animated path thumbnails. 2021-07-02 20:40:32 +03:00
John Preston
b22e2ffe1d Animate inline path thumbnails with sliding gradient. 2021-07-02 20:40:32 +03:00
Ilya Fedin
22d23c8be1 Add missed signalId check 2021-07-02 20:11:28 +03:00
Ilya Fedin
b335741f99 Use gsl::finally to pop thread context where appropriate 2021-07-02 20:11:28 +03:00
Ilya Fedin
1261c775d4 Fix freeze after creating file dialog 2021-07-02 15:51:58 +03:00
John Preston
01b4a24ac7 Save sticker path thumbnails to local storage. 2021-07-02 13:39:08 +03:00
John Preston
4124c2eb57 Show inline path thumbnails for stickers. 2021-07-02 13:13:48 +03:00
John Preston
f09b91ebb5 Beta version 2.8.5: Fix build on non-Windows. 2021-07-02 04:45:58 +03:00
John Preston
57b147e0c8 Beta version 2.8.5.
- Use ANGLE for OpenGL over DirectX 9 / DirectX 11 (Windows).
- Use GTK from a child process (Linux).
2021-07-02 00:59:51 +03:00
Ilya Fedin
551ea7d879 Move GTK integration out of process with D-Bus 2021-07-02 00:59:36 +03:00
John Preston
d3c9bb0bc6 Try disabling native child OpenGL workaround. 2021-07-02 00:37:27 +03:00
John Preston
c711b1f1df Fix build on non-Linux systems. 2021-07-02 00:27:25 +03:00
23rd
af7ea90246 Added floating panel of playing playlist for scheduled audio files. 2021-07-01 23:53:45 +03:00
23rd
348cf4829c Added ability to scroll media in section of scheduled messages.
Fixed #8388.
2021-07-01 23:53:45 +03:00
23rd
4753a57091 Added ability to validate playlists in section of scheduled messages. 2021-07-01 23:53:45 +03:00
23rd
1a93f4fa4c Added ability to sparse id slices of scheduled media. 2021-07-01 23:53:45 +03:00
23rd
118fd187e3 Added abstract class for sparse ids slices. 2021-07-01 23:53:45 +03:00
23rd
baa47bde7f Removed unused MsgRange from SparseIdsSlice. 2021-07-01 23:53:45 +03:00
John Preston
18b48df9ce Allow to choose ANGLE backend. 2021-07-01 23:48:18 +03:00
John Preston
e71fc60d22 Use exact revision of ANGLE. 2021-07-01 23:48:16 +03:00
John Preston
148af59615 Don't check dll-s if "SetDefaultDllDirectories" is available. 2021-07-01 23:47:12 +03:00
John Preston
5b2db4112f Don't allow any .dll-s near Telegram.exe 2021-07-01 23:47:12 +03:00
John Preston
7cedc1f7a5 Add dynamic DirectX loading helper. 2021-07-01 23:47:08 +03:00
John Preston
6cea7d4a52 Fix YUV->RGB on D3D9 ANGLE backend. 2021-07-01 23:46:52 +03:00
John Preston
bd93aed393 Test build with statically linked ANGLE. 2021-07-01 23:46:52 +03:00
John Preston
348666de6d Use media viewer size hack only when required. 2021-07-01 23:46:52 +03:00
Ilya Fedin
47e32bebe4 Remove not really needed gtk scale factor query 2021-07-01 22:13:50 +03:00
Ilya Fedin
0b21c04489 Remove the copy of gtk file dialog 2021-07-01 22:13:50 +03:00
Ilya Fedin
85f013ebdb Revert "Avoid removing portal platformtheme plugin in snap"
This reverts commit 12db51fe75.
2021-07-01 22:13:50 +03:00
Ilya Fedin
832cc6ac69 Build Qt with gtk integration 2021-07-01 22:13:50 +03:00
Ilya Fedin
30ce049f51 Update submodules 2021-07-01 22:13:20 +03:00
Ilya Fedin
d42fb6d1b9 Switch from mallocng to jemalloc
Now it's known how to make it free the memory in an expected manner and it's better maintained
2021-07-01 22:13:20 +03:00
John Preston
cade53aa0a Version 2.8.4.
- Crash fixes in WebView on Windows.
2021-07-01 11:05:21 +03:00
GitHub Action
2fdcda7536 Update User-Agent for DNS to Chrome 91.0.4472.114. 2021-07-01 10:48:17 +03:00
Ilya Fedin
7e6439e4f8 Fix counting screen bottom point when restoring geometry 2021-06-30 00:27:39 +03:00
Ilya Fedin
f07ee7f590 Update lib_base and cmake_helpers 2021-06-29 17:35:39 +03:00
Ilya Fedin
02db4e01fa Get rid of qt5ct 2021-06-29 17:35:39 +03:00
Ilya Fedin
8d75078a42 Use Glib::MainLoop instead of QEventLoop in glib code 2021-06-29 15:10:08 +03:00
John Preston
0e25ef7524 Handle WinRT exceptions in WebView. 2021-06-29 14:08:50 +03:00
John Preston
8608d8aa4d Crash Fix: Destroy WebView before the container. 2021-06-29 11:07:47 +03:00
sammiee5311
c1a7332a5e Shorten if statement
Shorten if statement
2021-06-29 10:38:35 +03:00
Ilya Fedin
c3fb392906 Clean dbus-specific code in main_window_linux.h 2021-06-29 10:30:48 +03:00
Ilya Fedin
a59bfdb2f8 Fix handleNativeSurfaceChanged when dbus integration is disabled 2021-06-29 10:30:48 +03:00
Ilya Fedin
79f96480c2 Use QMenuBar instead of own global menu implementation on Linux 2021-06-29 10:30:48 +03:00
John Preston
60cbd96d91 Version 2.8.3.
- Fix crashes in OpenSSL on macOS.
2021-06-28 13:51:06 +03:00
274 changed files with 9748 additions and 3554 deletions

View File

@@ -93,6 +93,9 @@ jobs:
DEFINE=""
if [ -n "${{ matrix.defines }}" ]; then
DEFINE="-D ${{ matrix.defines }}=ON"
if [ "${{ matrix.defines }}" == "DESKTOP_APP_DISABLE_DBUS_INTEGRATION" ]; then
DEFINE="$DEFINE -D DESKTOP_APP_DISABLE_GTK_INTEGRATION=ON"
fi
echo Define from matrix: $DEFINE
echo "ARTIFACT_NAME=Telegram_${{ matrix.defines }}" >> $GITHUB_ENV
else

6
.gitmodules vendored
View File

@@ -76,9 +76,6 @@
[submodule "Telegram/ThirdParty/hime"]
path = Telegram/ThirdParty/hime
url = https://github.com/hime-ime/hime.git
[submodule "Telegram/ThirdParty/qt5ct"]
path = Telegram/ThirdParty/qt5ct
url = https://github.com/desktop-app/qt5ct.git
[submodule "Telegram/ThirdParty/fcitx5-qt"]
path = Telegram/ThirdParty/fcitx5-qt
url = https://github.com/fcitx/fcitx5-qt.git
@@ -91,9 +88,6 @@
[submodule "Telegram/lib_webview"]
path = Telegram/lib_webview
url = https://github.com/desktop-app/lib_webview.git
[submodule "Telegram/ThirdParty/mallocng"]
path = Telegram/ThirdParty/mallocng
url = https://github.com/desktop-app/mallocng.git
[submodule "Telegram/lib_waylandshells"]
path = Telegram/lib_waylandshells
url = https://github.com/desktop-app/lib_waylandshells.git

View File

@@ -87,7 +87,6 @@ if (LINUX)
PRIVATE
desktop-app::external_glibmm
desktop-app::external_glib
desktop-app::external_mallocng
)
if (NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
@@ -114,6 +113,7 @@ if (LINUX)
endif()
if (NOT DESKTOP_APP_DISABLE_GTK_INTEGRATION)
target_link_libraries(Telegram PRIVATE rt)
find_package(PkgConfig REQUIRED)
if (DESKTOP_APP_USE_PACKAGED AND NOT DESKTOP_APP_USE_PACKAGED_LAZY)
@@ -251,8 +251,6 @@ PRIVATE
boxes/peer_lists_box.h
boxes/passcode_box.cpp
boxes/passcode_box.h
boxes/photo_crop_box.cpp
boxes/photo_crop_box.h
boxes/rate_call_box.cpp
boxes/rate_call_box.h
boxes/self_destruction_box.cpp
@@ -395,6 +393,7 @@ PRIVATE
data/stickers/data_stickers_set.h
data/stickers/data_stickers.cpp
data/stickers/data_stickers.h
data/data_abstract_sparse_ids.h
data/data_abstract_structure.cpp
data/data_abstract_structure.h
data/data_auto_download.cpp
@@ -507,6 +506,39 @@ PRIVATE
dialogs/dialogs_search_from_controllers.h
dialogs/dialogs_widget.cpp
dialogs/dialogs_widget.h
editor/color_picker.cpp
editor/color_picker.h
editor/controllers/controllers.h
editor/controllers/stickers_panel_controller.cpp
editor/controllers/stickers_panel_controller.h
editor/controllers/undo_controller.cpp
editor/controllers/undo_controller.h
editor/editor_crop.cpp
editor/editor_crop.h
editor/editor_paint.cpp
editor/editor_paint.h
editor/photo_editor.cpp
editor/photo_editor.h
editor/photo_editor_common.cpp
editor/photo_editor_common.h
editor/photo_editor_content.cpp
editor/photo_editor_content.h
editor/photo_editor_controls.cpp
editor/photo_editor_controls.h
editor/photo_editor_layer_widget.cpp
editor/photo_editor_layer_widget.h
editor/scene/scene.cpp
editor/scene/scene.h
editor/scene/scene_item_base.cpp
editor/scene/scene_item_base.h
editor/scene/scene_item_canvas.cpp
editor/scene/scene_item_canvas.h
editor/scene/scene_item_image.cpp
editor/scene/scene_item_image.h
editor/scene/scene_item_line.cpp
editor/scene/scene_item_line.h
editor/scene/scene_item_sticker.cpp
editor/scene/scene_item_sticker.h
export/export_manager.cpp
export/export_manager.h
export/view/export_view_content.cpp
@@ -863,8 +895,6 @@ PRIVATE
platform/linux/linux_gdk_helper.h
platform/linux/linux_gsd_media_keys.cpp
platform/linux/linux_gsd_media_keys.h
platform/linux/linux_gtk_file_dialog.cpp
platform/linux/linux_gtk_file_dialog.h
platform/linux/linux_gtk_integration_dummy.cpp
platform/linux/linux_gtk_integration_p.h
platform/linux/linux_gtk_integration.cpp
@@ -1196,8 +1226,6 @@ if (DESKTOP_APP_DISABLE_GTK_INTEGRATION)
remove_target_sources(Telegram ${src_loc}
platform/linux/linux_gdk_helper.cpp
platform/linux/linux_gdk_helper.h
platform/linux/linux_gtk_file_dialog.cpp
platform/linux/linux_gtk_file_dialog.h
platform/linux/linux_gtk_integration_p.h
platform/linux/linux_gtk_integration.cpp
platform/linux/linux_gtk_open_with_dialog.cpp
@@ -1362,6 +1390,32 @@ endif()
set_target_properties(Telegram PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder})
if (WIN32)
target_link_options(Telegram
PRIVATE
/DELAYLOAD:secur32.dll
/DELAYLOAD:winmm.dll
/DELAYLOAD:ws2_32.dll
/DELAYLOAD:user32.dll
/DELAYLOAD:gdi32.dll
/DELAYLOAD:advapi32.dll
/DELAYLOAD:shell32.dll
/DELAYLOAD:ole32.dll
/DELAYLOAD:oleaut32.dll
/DELAYLOAD:shlwapi.dll
/DELAYLOAD:iphlpapi.dll
/DELAYLOAD:gdiplus.dll
/DELAYLOAD:version.dll
/DELAYLOAD:dwmapi.dll
/DELAYLOAD:crypt32.dll
/DELAYLOAD:bcrypt.dll
/DELAYLOAD:imm32.dll
/DELAYLOAD:netapi32.dll
/DELAYLOAD:userenv.dll
/DELAYLOAD:wtsapi32.dll
)
endif()
if ((NOT DESKTOP_APP_DISABLE_AUTOUPDATE OR APPLE) AND NOT build_macstore AND NOT build_winstore)
add_executable(Updater WIN32)
init_target(Updater)
@@ -1378,8 +1432,26 @@ if ((NOT DESKTOP_APP_DISABLE_AUTOUPDATE OR APPLE) AND NOT build_macstore AND NOT
set_target_properties(Updater PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder})
if (WIN32 AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_link_options(Updater PRIVATE -municode)
if (WIN32)
get_filename_component(lib_base_loc lib_base REALPATH)
nice_target_sources(Updater ${lib_base_loc}
PRIVATE
base/platform/win/base_windows_safe_library.cpp
base/platform/win/base_windows_safe_library.h
)
target_include_directories(Updater PRIVATE ${lib_base_loc})
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_link_options(Updater
PRIVATE
/DELAYLOAD:user32.dll
/DELAYLOAD:advapi32.dll
/DELAYLOAD:shell32.dll
/DELAYLOAD:ole32.dll
/DELAYLOAD:shlwapi.dll
)
else()
target_link_options(Updater PRIVATE -municode)
endif()
endif()
if (LINUX)

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 947 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -184,6 +184,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_media_album_error" = "This file cannot be saved as a part of an album.";
"lng_edit_media_invalid_file" = "Sorry, no way to use this file.";
"lng_edit_photo_editor_hint" = "Left-click on the photo to edit.";
"lng_edit_caption_attach" = "Sorry, you can't attach a new media while you're editing your message.";
"lng_edit_caption_voice" = "Sorry, you can't edit your message while you're having an unsent voice message.";
@@ -452,6 +453,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_performance" = "Performance";
"lng_settings_enable_animations" = "Enable animations";
"lng_settings_enable_opengl" = "Enable OpenGL rendering for media";
"lng_settings_angle_backend" = "ANGLE graphics backend";
"lng_settings_angle_backend_auto" = "Auto";
"lng_settings_angle_backend_d3d9" = "Direct3D 9";
"lng_settings_angle_backend_d3d11" = "Direct3D 11";
"lng_settings_angle_backend_d3d11on12" = "D3D11on12";
"lng_settings_angle_backend_opengl" = "OpenGL";
"lng_settings_angle_backend_disabled" = "Disabled";
"lng_settings_sensitive_title" = "Sensitive content";
"lng_settings_sensitive_disable_filtering" = "Disable filtering";
"lng_settings_sensitive_about" = "Display sensitive media in public channels on all your Telegram devices.";
@@ -1372,6 +1380,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_switch_stickers" = "Stickers";
"lng_switch_emoji" = "Emoji";
"lng_switch_gifs" = "GIFs";
"lng_switch_masks" = "Masks";
"lng_stickers_featured_add" = "Add";
"lng_gifs_search" = "Search GIFs";
"lng_gifs_no_saved" = "You have no saved GIFs yet.";
@@ -1382,11 +1391,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_box_remove" = "Remove";
"lng_stickers_installed_tab" = "Stickers";
"lng_stickers_masks_tab" = "Masks";
"lng_stickers_featured_tab" = "Trending";
"lng_stickers_archived_tab" = "Archived";
"lng_stickers_remove_pack" = "Remove «{sticker_pack}»?";
"lng_stickers_add_pack" = "Add stickers";
"lng_stickers_add_masks" = "Add masks";
"lng_stickers_share_pack" = "Share Stickers";
"lng_stickers_share_masks" = "Share Masks";
"lng_stickers_not_found" = "Sticker pack not found.";
"lng_stickers_packs_archived" = "Some of your unused stickers have been archived to make room for the sets you've activated.";
"lng_stickers_copied" = "Sticker pack link copied to clipboard.";
@@ -1395,7 +1407,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stickers_return" = "Undo";
"lng_stickers_count#one" = "{count} sticker";
"lng_stickers_count#other" = "{count} stickers";
"lng_stickers_masks_pack" = "This is a pack of mask stickers. You can use them in the photo editor on our mobile apps.";
"lng_masks_count#one" = "{count} mask";
"lng_masks_count#other" = "{count} masks";
"lng_stickers_attached_sets" = "Sets of attached stickers";
"lng_stickers_group_set" = "Group sticker set";
"lng_stickers_remove_group_set" = "Remove group sticker set?";
@@ -1406,6 +1419,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stickers_remove_pack_confirm" = "Remove";
"lng_stickers_archive_pack" = "Archive Stickers";
"lng_stickers_has_been_archived" = "Sticker pack has been archived.";
"lng_masks_archive_pack" = "Archive Masks";
"lng_masks_has_been_archived" = "Mask pack has been archived.";
"lng_masks_installed" = "Mask pack has been installed.";
"lng_in_dlg_photo" = "Photo";
"lng_in_dlg_album" = "Album";
@@ -2760,6 +2776,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_filters_remove_sure" = "This will remove the folder, your chats will not be deleted.";
"lng_filters_remove_yes" = "Remove";
"lng_photo_editor_menu_delete" = "Delete";
"lng_photo_editor_menu_flip" = "Flip";
"lng_photo_editor_menu_duplicate" = "Duplicate";
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";

View File

@@ -579,8 +579,8 @@ keyboardButtonRequestPoll#bbc7515d flags:# quiz:flags.0?Bool text:string = Keybo
keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
replyKeyboardHide#a03e5b85 flags:# selective:flags.2?true = ReplyMarkup;
replyKeyboardForceReply#f4108aa0 flags:# single_use:flags.1?true selective:flags.2?true = ReplyMarkup;
replyKeyboardMarkup#3502758c flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true rows:Vector<KeyboardButtonRow> = ReplyMarkup;
replyKeyboardForceReply#86b40b08 flags:# single_use:flags.1?true selective:flags.2?true placeholder:flags.3?string = ReplyMarkup;
replyKeyboardMarkup#85dd99d1 flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true rows:Vector<KeyboardButtonRow> placeholder:flags.3?string = ReplyMarkup;
replyInlineMarkup#48a30254 rows:Vector<KeyboardButtonRow> = ReplyMarkup;
messageEntityUnknown#bb92ba95 offset:int length:int = MessageEntity;
@@ -1250,6 +1250,16 @@ groupCallParticipantVideoSourceGroup#dcb118b7 semantics:string sources:Vector<in
groupCallParticipantVideo#78e41663 flags:# paused:flags.0?true endpoint:string source_groups:Vector<GroupCallParticipantVideoSourceGroup> = GroupCallParticipantVideo;
stickers.suggestedShortName#85fea03f short_name:string = stickers.SuggestedShortName;
botCommandScopeDefault#2f6cb2ab = BotCommandScope;
botCommandScopeUsers#3c4f04d8 = BotCommandScope;
botCommandScopeChats#6fe1a881 = BotCommandScope;
botCommandScopeChatAdmins#b9aa606a = BotCommandScope;
botCommandScopePeer#db9d897d peer:InputPeer = BotCommandScope;
botCommandScopePeerAdmins#3fd863d1 peer:InputPeer = BotCommandScope;
botCommandScopePeerUser#a1321f3 peer:InputPeer user_id:InputUser = BotCommandScope;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -1596,7 +1606,9 @@ channels.convertToGigagroup#b290c69 channel:InputChannel = Updates;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
bots.setBotCommands#805d46f6 commands:Vector<BotCommand> = Bool;
bots.setBotCommands#517165a scope:BotCommandScope lang_code:string commands:Vector<BotCommand> = Bool;
bots.resetBotCommands#3d8de0f9 scope:BotCommandScope lang_code:string = Bool;
bots.getBotCommands#e34c0dd6 scope:BotCommandScope lang_code:string = Vector<BotCommand>;
payments.getPaymentForm#8a333c8d flags:# peer:InputPeer msg_id:int theme_params:flags.0?DataJSON = payments.PaymentForm;
payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt;
@@ -1606,11 +1618,13 @@ payments.getSavedInfo#227d824b = payments.SavedInfo;
payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool;
payments.getBankCardData#2e79d779 number:string = payments.BankCardData;
stickers.createStickerSet#f1036780 flags:# masks:flags.0?true animated:flags.1?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> = messages.StickerSet;
stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true animated:flags.1?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> software:flags.3?string = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;
stickers.changeStickerPosition#ffb6d4ca sticker:InputDocument position:int = messages.StickerSet;
stickers.addStickerToSet#8653febe stickerset:InputStickerSet sticker:InputStickerSetItem = messages.StickerSet;
stickers.setStickerSetThumb#9a364e30 stickerset:InputStickerSet thumb:InputDocument = messages.StickerSet;
stickers.checkShortName#284b3639 short_name:string = Bool;
stickers.suggestShortName#4dafc503 title:string = stickers.SuggestedShortName;
phone.getCallConfig#55451fa9 = DataJSON;
phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
@@ -1656,4 +1670,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
// LAYER 129
// LAYER 130

View File

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

View File

@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,8,2,0
PRODUCTVERSION 2,8,2,0
FILEVERSION 2,8,6,0
PRODUCTVERSION 2,8,6,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "2.8.2.0"
VALUE "FileVersion", "2.8.6.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.8.2.0"
VALUE "ProductVersion", "2.8.6.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,8,2,0
PRODUCTVERSION 2,8,2,0
FILEVERSION 2,8,6,0
PRODUCTVERSION 2,8,6,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "2.8.2.0"
VALUE "FileVersion", "2.8.6.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.8.2.0"
VALUE "ProductVersion", "2.8.6.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "updater.h"
#include "base/platform/win/base_windows_safe_library.h"
bool _debug = false;
wstring updaterName, updaterDir, updateTo, exeName, customWorkingDir, customKeyFile;
@@ -329,6 +331,8 @@ void updateRegistry() {
}
int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdParamarg, int cmdShow) {
base::Platform::InitDynamicLibraries();
openLog();
_oldWndExceptionFilter = SetUnhandledExceptionFilter(_exceptionFilter);

View File

@@ -52,6 +52,10 @@ mtpRequestId EditMessage(
ConvertOption::SkipLocal);
const auto media = item->media();
const auto updateRecentStickers = inputMedia.has_value()
? Api::HasAttachedStickers(*inputMedia)
: false;
const auto emptyFlag = MTPmessages_EditMessage::Flag(0);
const auto flags = emptyFlag
| (!text.isEmpty() || media
@@ -97,6 +101,10 @@ mtpRequestId EditMessage(
} else {
apply();
}
if (updateRecentStickers) {
api->requestRecentStickersForce(true);
}
}).fail(
fail
).send();
@@ -165,22 +173,30 @@ void EditMessageWithUploadedDocument(
HistoryItem *item,
const MTPInputFile &file,
const std::optional<MTPInputFile> &thumb,
SendOptions options) {
SendOptions options,
std::vector<MTPInputDocument> attachedStickers) {
if (!item || !item->media() || !item->media()->document()) {
return;
}
const auto media = PrepareUploadedDocument(item, file, thumb);
const auto media = PrepareUploadedDocument(
item,
file,
thumb,
std::move(attachedStickers));
EditMessageWithUploadedMedia(item, options, media);
}
void EditMessageWithUploadedPhoto(
HistoryItem *item,
const MTPInputFile &file,
SendOptions options) {
SendOptions options,
std::vector<MTPInputDocument> attachedStickers) {
if (!item || !item->media() || !item->media()->photo()) {
return;
}
const auto media = PrepareUploadedPhoto(file);
const auto media = PrepareUploadedPhoto(
file,
std::move(attachedStickers));
EditMessageWithUploadedMedia(item, options, media);
}

View File

@@ -31,12 +31,14 @@ void EditMessageWithUploadedDocument(
HistoryItem *item,
const MTPInputFile &file,
const std::optional<MTPInputFile> &thumb,
SendOptions options);
SendOptions options,
std::vector<MTPInputDocument> attachedStickers);
void EditMessageWithUploadedPhoto(
HistoryItem *item,
const MTPInputFile &file,
SendOptions options);
SendOptions options,
std::vector<MTPInputDocument> attachedStickers);
mtpRequestId EditCaption(
not_null<HistoryItem*> item,

View File

@@ -61,10 +61,14 @@ int32 CountStickersHash(
: 0;
}
int32 CountRecentStickersHash(not_null<Main::Session*> session) {
int32 CountRecentStickersHash(
not_null<Main::Session*> session,
bool attached) {
return CountSpecialStickerSetHash(
session,
Data::Stickers::CloudRecentSetId);
attached
? Data::Stickers::CloudRecentAttachedSetId
: Data::Stickers::CloudRecentSetId);
}
int32 CountFavedStickersHash(not_null<Main::Session*> session) {

View File

@@ -17,7 +17,8 @@ namespace Api {
not_null<Main::Session*> session,
bool checkOutdatedInfo = false);
[[nodiscard]] int32 CountRecentStickersHash(
not_null<Main::Session*> session);
not_null<Main::Session*> session,
bool attached = false);
[[nodiscard]] int32 CountFavedStickersHash(not_null<Main::Session*> session);
[[nodiscard]] int32 CountFeaturedStickersHash(
not_null<Main::Session*> session);

View File

@@ -73,18 +73,24 @@ MTPVector<MTPDocumentAttribute> ComposeSendingDocumentAttributes(
} // namespace
MTPInputMedia PrepareUploadedPhoto(const MTPInputFile &file) {
MTPInputMedia PrepareUploadedPhoto(
const MTPInputFile &file,
std::vector<MTPInputDocument> attachedStickers) {
const auto flags = attachedStickers.empty()
? MTPDinputMediaUploadedPhoto::Flags(0)
: MTPDinputMediaUploadedPhoto::Flag::f_stickers;
return MTP_inputMediaUploadedPhoto(
MTP_flags(0),
MTP_flags(flags),
file,
MTPVector<MTPInputDocument>(),
MTP_vector<MTPInputDocument>(ranges::to<QVector>(attachedStickers)),
MTP_int(0));
}
MTPInputMedia PrepareUploadedDocument(
not_null<HistoryItem*> item,
const MTPInputFile &file,
const std::optional<MTPInputFile> &thumb) {
const std::optional<MTPInputFile> &thumb,
std::vector<MTPInputDocument> attachedStickers) {
if (!item || !item->media() || !item->media()->document()) {
return MTP_inputMediaEmpty();
}
@@ -92,7 +98,8 @@ MTPInputMedia PrepareUploadedDocument(
using DocFlags = MTPDinputMediaUploadedDocument::Flag;
const auto flags = emptyFlag
| (thumb ? DocFlags::f_thumb : emptyFlag)
| (item->groupId() ? DocFlags::f_nosound_video : emptyFlag);
| (item->groupId() ? DocFlags::f_nosound_video : emptyFlag)
| (attachedStickers.empty() ? DocFlags::f_stickers : emptyFlag);
const auto document = item->media()->document();
return MTP_inputMediaUploadedDocument(
MTP_flags(flags),
@@ -100,8 +107,20 @@ MTPInputMedia PrepareUploadedDocument(
thumb.value_or(MTPInputFile()),
MTP_string(document->mimeString()),
ComposeSendingDocumentAttributes(document),
MTPVector<MTPInputDocument>(),
MTP_vector<MTPInputDocument>(ranges::to<QVector>(attachedStickers)),
MTP_int(0));
}
bool HasAttachedStickers(MTPInputMedia media) {
return media.match([&](const MTPDinputMediaUploadedPhoto &photo) -> bool {
return (photo.vflags().v
& MTPDinputMediaUploadedPhoto::Flag::f_stickers);
}, [&](const MTPDinputMediaUploadedDocument &document) -> bool {
return (document.vflags().v
& MTPDinputMediaUploadedDocument::Flag::f_stickers);
}, [](const auto &d) {
return false;
});
}
} // namespace Api

View File

@@ -11,11 +11,16 @@ class HistoryItem;
namespace Api {
MTPInputMedia PrepareUploadedPhoto(const MTPInputFile &file);
MTPInputMedia PrepareUploadedPhoto(
const MTPInputFile &file,
std::vector<MTPInputDocument> attachedStickers);
MTPInputMedia PrepareUploadedDocument(
not_null<HistoryItem*> item,
const MTPInputFile &file,
const std::optional<MTPInputFile> &thumb);
const std::optional<MTPInputFile> &thumb,
std::vector<MTPInputDocument> attachedStickers);
bool HasAttachedStickers(MTPInputMedia media);
} // namespace Api

View File

@@ -2141,31 +2141,46 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateStickerSetsOrder: {
auto &d = update.c_updateStickerSetsOrder();
if (!d.is_masks()) {
const auto &order = d.vorder().v;
const auto &sets = session().data().stickers().sets();
Data::StickersSetsOrder result;
for (const auto &item : order) {
if (sets.find(item.v) == sets.cend()) {
break;
}
result.push_back(item.v);
auto &stickers = session().data().stickers();
const auto isMasks = d.is_masks();
const auto &order = d.vorder().v;
const auto &sets = stickers.sets();
Data::StickersSetsOrder result;
for (const auto &item : order) {
if (sets.find(item.v) == sets.cend()) {
break;
}
if (result.size() != session().data().stickers().setsOrder().size()
|| result.size() != order.size()) {
session().data().stickers().setLastUpdate(0);
session().api().updateStickers();
result.push_back(item.v);
}
const auto localSize = isMasks
? stickers.maskSetsOrder().size()
: stickers.setsOrder().size();
if ((result.size() != localSize) || (result.size() != order.size())) {
if (isMasks) {
stickers.setLastMasksUpdate(0);
session().api().updateMasks();
} else {
session().data().stickers().setsOrderRef() = std::move(result);
session().local().writeInstalledStickers();
session().data().stickers().notifyUpdated();
stickers.setLastUpdate(0);
session().api().updateStickers();
}
} else {
if (isMasks) {
stickers.maskSetsOrderRef() = std::move(result);
session().local().writeInstalledMasks();
} else {
stickers.setsOrderRef() = std::move(result);
session().local().writeInstalledStickers();
}
stickers.notifyUpdated();
}
} break;
case mtpc_updateStickerSets: {
// Can't determine is it masks or stickers, so update both.
session().data().stickers().setLastUpdate(0);
session().api().updateStickers();
session().data().stickers().setLastMasksUpdate(0);
session().api().updateMasks();
} break;
case mtpc_updateRecentStickers: {

View File

@@ -1857,9 +1857,14 @@ void ApiWrap::requestStickerSets() {
if (i.value().second) continue;
auto waitMs = (j == e) ? 0 : kSmallDelayMs;
i.value().second = request(MTPmessages_GetStickerSet(MTP_inputStickerSetID(MTP_long(i.key()), MTP_long(i.value().first)))).done([this, setId = i.key()](const MTPmessages_StickerSet &result) {
const auto id = MTP_inputStickerSetID(
MTP_long(i.key()),
MTP_long(i.value().first));
i.value().second = request(MTPmessages_GetStickerSet(
id
)).done([=, setId = i.key()](const MTPmessages_StickerSet &result) {
gotStickerSet(setId, result);
}).fail([this, setId = i.key()](const MTP::Error &error) {
}).fail([=, setId = i.key()](const MTP::Error &error) {
_stickerSetRequests.remove(setId);
}).afterDelay(waitMs).send();
}
@@ -1867,23 +1872,91 @@ void ApiWrap::requestStickerSets() {
void ApiWrap::saveStickerSets(
const Data::StickersSetsOrder &localOrder,
const Data::StickersSetsOrder &localRemoved) {
for (auto requestId : base::take(_stickerSetDisenableRequests)) {
const Data::StickersSetsOrder &localRemoved,
bool setsMasks) {
auto &setDisenableRequests = setsMasks
? _maskSetDisenableRequests
: _stickerSetDisenableRequests;
const auto reorderRequestId = [=]() -> mtpRequestId & {
return setsMasks
? _masksReorderRequestId
: _stickersReorderRequestId;
};
for (auto requestId : base::take(setDisenableRequests)) {
request(requestId).cancel();
}
request(base::take(_stickersReorderRequestId)).cancel();
request(base::take(reorderRequestId())).cancel();
request(base::take(_stickersClearRecentRequestId)).cancel();
request(base::take(_stickersClearRecentAttachedRequestId)).cancel();
auto writeInstalled = true, writeRecent = false, writeCloudRecent = false, writeFaved = false, writeArchived = false;
const auto stickersSaveOrder = [=] {
if (localOrder.size() < 2) {
return;
}
QVector<MTPlong> mtpOrder;
mtpOrder.reserve(localOrder.size());
for (const auto setId : std::as_const(localOrder)) {
mtpOrder.push_back(MTP_long(setId));
}
const auto flags = setsMasks
? MTPmessages_ReorderStickerSets::Flag::f_masks
: MTPmessages_ReorderStickerSets::Flags(0);
reorderRequestId() = request(MTPmessages_ReorderStickerSets(
MTP_flags(flags),
MTP_vector<MTPlong>(mtpOrder)
)).done([=](const MTPBool &result) {
reorderRequestId() = 0;
}).fail([=](const MTP::Error &error) {
reorderRequestId() = 0;
if (setsMasks) {
_session->data().stickers().setLastMasksUpdate(0);
updateMasks();
} else {
_session->data().stickers().setLastUpdate(0);
updateStickers();
}
}).send();
};
const auto stickerSetDisenabled = [=](mtpRequestId requestId) {
auto &setDisenableRequests = setsMasks
? _maskSetDisenableRequests
: _stickerSetDisenableRequests;
setDisenableRequests.remove(requestId);
if (setDisenableRequests.empty()) {
stickersSaveOrder();
}
};
auto writeInstalled = true,
writeRecent = false,
writeCloudRecent = false,
writeCloudRecentAttached = false,
writeFaved = false,
writeArchived = false;
auto &recent = _session->data().stickers().getRecentPack();
auto &sets = _session->data().stickers().setsRef();
_stickersOrder = localOrder;
auto &order = setsMasks
? _session->data().stickers().maskSetsOrder()
: _session->data().stickers().setsOrder();
auto &orderRef = setsMasks
? _session->data().stickers().maskSetsOrderRef()
: _session->data().stickers().setsOrderRef();
using Flag = MTPDstickerSet::Flag;
using ClientFlag = MTPDstickerSet_ClientFlag;
for (const auto removedSetId : localRemoved) {
if (removedSetId == Data::Stickers::CloudRecentSetId) {
if ((removedSetId == Data::Stickers::CloudRecentSetId)
|| (removedSetId == Data::Stickers::CloudRecentAttachedSetId)) {
if (sets.remove(Data::Stickers::CloudRecentSetId) != 0) {
writeCloudRecent = true;
}
if (sets.remove(Data::Stickers::CloudRecentAttachedSetId) != 0) {
writeCloudRecentAttached = true;
}
if (sets.remove(Data::Stickers::CustomSetId)) {
writeInstalled = true;
}
@@ -1892,12 +1965,25 @@ void ApiWrap::saveStickerSets(
writeRecent = true;
}
_stickersClearRecentRequestId = request(MTPmessages_ClearRecentStickers(
MTP_flags(0)
)).done([this](const MTPBool &result) {
_stickersClearRecentRequestId = 0;
}).fail([this](const MTP::Error &error) {
_stickersClearRecentRequestId = 0;
const auto isAttached =
(removedSetId == Data::Stickers::CloudRecentAttachedSetId);
const auto flags = isAttached
? MTPmessages_ClearRecentStickers::Flag::f_attached
: MTPmessages_ClearRecentStickers::Flags(0);
auto &requestId = isAttached
? _stickersClearRecentAttachedRequestId
: _stickersClearRecentRequestId;
const auto finish = [=] {
(isAttached
? _stickersClearRecentAttachedRequestId
: _stickersClearRecentRequestId) = 0;
};
requestId = request(MTPmessages_ClearRecentStickers(
MTP_flags(flags)
)).done([=](const MTPBool &result) {
finish();
}).fail([=](const MTP::Error &error) {
finish();
}).send();
continue;
}
@@ -1913,27 +1999,34 @@ void ApiWrap::saveStickerSets(
++i;
}
}
if (!(set->flags & MTPDstickerSet::Flag::f_archived)) {
const auto archived = !!(set->flags & Flag::f_archived);
if (!archived) {
const auto featured = !!(set->flags & ClientFlag::f_featured);
const auto special = !!(set->flags & ClientFlag::f_special);
const auto setId = set->mtpInput();
auto requestId = request(MTPmessages_UninstallStickerSet(setId)).done([this](const MTPBool &result, mtpRequestId requestId) {
auto requestId = request(MTPmessages_UninstallStickerSet(
setId
)).done([=](const MTPBool &result, mtpRequestId requestId) {
stickerSetDisenabled(requestId);
}).fail([this](const MTP::Error &error, mtpRequestId requestId) {
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
stickerSetDisenabled(requestId);
}).afterDelay(kSmallDelayMs).send();
_stickerSetDisenableRequests.insert(requestId);
setDisenableRequests.insert(requestId);
int removeIndex = _session->data().stickers().setsOrder().indexOf(set->id);
if (removeIndex >= 0) _session->data().stickers().setsOrderRef().removeAt(removeIndex);
if (!(set->flags & MTPDstickerSet_ClientFlag::f_featured)
&& !(set->flags & MTPDstickerSet_ClientFlag::f_special)) {
const auto removeIndex = order.indexOf(set->id);
if (removeIndex >= 0) {
orderRef.removeAt(removeIndex);
}
if (!featured && !special) {
sets.erase(it);
} else {
if (set->flags & MTPDstickerSet::Flag::f_archived) {
if (archived) {
writeArchived = true;
}
set->flags &= ~(MTPDstickerSet::Flag::f_installed_date | MTPDstickerSet::Flag::f_archived);
set->flags &= ~(Flag::f_installed_date
| Flag::f_archived);
set->installDate = TimeId(0);
}
}
@@ -1942,51 +2035,55 @@ void ApiWrap::saveStickerSets(
// Clear all installed flags, set only for sets from order.
for (auto &[id, set] : sets) {
if (!(set->flags & MTPDstickerSet::Flag::f_archived)) {
set->flags &= ~MTPDstickerSet::Flag::f_installed_date;
const auto archived = !!(set->flags & Flag::f_archived);
const auto masks = !!(set->flags & MTPDstickerSet::Flag::f_masks);
if (!archived && (setsMasks == masks)) {
set->flags &= ~Flag::f_installed_date;
}
}
auto &order = _session->data().stickers().setsOrderRef();
order.clear();
for (const auto setId : std::as_const(_stickersOrder)) {
orderRef.clear();
for (const auto setId : std::as_const(localOrder)) {
auto it = sets.find(setId);
if (it != sets.cend()) {
const auto set = it->second.get();
if ((set->flags & MTPDstickerSet::Flag::f_archived) && !localRemoved.contains(set->id)) {
const auto mtpSetId = set->mtpInput();
if (it == sets.cend()) {
continue;
}
const auto set = it->second.get();
const auto archived = !!(set->flags & Flag::f_archived);
if (archived && !localRemoved.contains(set->id)) {
const auto mtpSetId = set->mtpInput();
const auto requestId = request(MTPmessages_InstallStickerSet(
mtpSetId,
MTP_boolFalse()
)).done([=](
const MTPmessages_StickerSetInstallResult &result,
mtpRequestId requestId) {
stickerSetDisenabled(requestId);
}).fail([=](
const MTP::Error &error,
mtpRequestId requestId) {
stickerSetDisenabled(requestId);
}).afterDelay(kSmallDelayMs).send();
const auto requestId = request(MTPmessages_InstallStickerSet(
mtpSetId,
MTP_boolFalse()
)).done([=](
const MTPmessages_StickerSetInstallResult &result,
mtpRequestId requestId) {
stickerSetDisenabled(requestId);
}).fail([=](
const MTP::Error &error,
mtpRequestId requestId) {
stickerSetDisenabled(requestId);
}).afterDelay(kSmallDelayMs).send();
_stickerSetDisenableRequests.insert(requestId);
setDisenableRequests.insert(requestId);
set->flags &= ~MTPDstickerSet::Flag::f_archived;
writeArchived = true;
}
order.push_back(setId);
set->flags |= MTPDstickerSet::Flag::f_installed_date;
if (!set->installDate) {
set->installDate = base::unixtime::now();
}
set->flags &= ~Flag::f_archived;
writeArchived = true;
}
orderRef.push_back(setId);
set->flags |= Flag::f_installed_date;
if (!set->installDate) {
set->installDate = base::unixtime::now();
}
}
for (auto it = sets.begin(); it != sets.cend();) {
const auto set = it->second.get();
if ((set->flags & MTPDstickerSet_ClientFlag::f_featured)
|| (set->flags & MTPDstickerSet::Flag::f_installed_date)
|| (set->flags & MTPDstickerSet::Flag::f_archived)
|| (set->flags & MTPDstickerSet_ClientFlag::f_special)) {
if ((set->flags & ClientFlag::f_featured)
|| (set->flags & Flag::f_installed_date)
|| (set->flags & Flag::f_archived)
|| (set->flags & ClientFlag::f_special)) {
++it;
} else {
it = sets.erase(it);
@@ -1994,27 +2091,40 @@ void ApiWrap::saveStickerSets(
}
auto &storage = local();
if (writeInstalled) storage.writeInstalledStickers();
if (writeRecent) session().saveSettings();
if (writeArchived) storage.writeArchivedStickers();
if (writeCloudRecent) storage.writeRecentStickers();
if (writeFaved) storage.writeFavedStickers();
if (writeInstalled && !setsMasks) {
storage.writeInstalledStickers();
}
if (writeInstalled && setsMasks) {
storage.writeInstalledMasks();
}
if (writeRecent) {
session().saveSettings();
}
if (writeArchived) {
if (setsMasks) {
storage.writeArchivedMasks();
} else {
storage.writeArchivedStickers();
}
}
if (writeCloudRecent) {
storage.writeRecentStickers();
}
if (writeCloudRecentAttached) {
storage.writeRecentMasks();
}
if (writeFaved) {
storage.writeFavedStickers();
}
_session->data().stickers().notifyUpdated();
if (_stickerSetDisenableRequests.empty()) {
if (setDisenableRequests.empty()) {
stickersSaveOrder();
} else {
requestSendDelayed();
}
}
void ApiWrap::stickerSetDisenabled(mtpRequestId requestId) {
_stickerSetDisenableRequests.remove(requestId);
if (_stickerSetDisenableRequests.empty()) {
stickersSaveOrder();
}
};
void ApiWrap::joinChannel(not_null<ChannelData*> channel) {
if (channel->amIn()) {
session().changes().peerUpdated(
@@ -2825,12 +2935,24 @@ void ApiWrap::refreshFileReference(
}, [&](Data::FileOriginPeerPhoto data) {
fail();
}, [&](Data::FileOriginStickerSet data) {
const auto isRecentAttached =
(data.setId == Data::Stickers::CloudRecentAttachedSetId);
if (data.setId == Data::Stickers::CloudRecentSetId
|| data.setId == Data::Stickers::RecentSetId) {
|| data.setId == Data::Stickers::RecentSetId
|| isRecentAttached) {
auto done = [=] { crl::on_main(_session, [=] {
if (isRecentAttached) {
local().writeRecentMasks();
} else {
local().writeRecentStickers();
}
}); };
request(MTPmessages_GetRecentStickers(
MTP_flags(0),
MTP_flags(isRecentAttached
? MTPmessages_GetRecentStickers::Flag::f_attached
: MTPmessages_GetRecentStickers::Flags(0)),
MTP_int(0)),
[=] { crl::on_main(_session, [=] { local().writeRecentStickers(); }); });
std::move(done));
} else if (data.setId == Data::Stickers::FavedSetId) {
request(MTPmessages_GetFavedStickers(MTP_int(0)),
[=] { crl::on_main(_session, [=] { local().writeFavedStickers(); }); });
@@ -2886,30 +3008,8 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &resu
_session->data().sendWebPageGamePollNotifications();
}
void ApiWrap::stickersSaveOrder() {
if (_stickersOrder.size() < 2) {
return;
}
QVector<MTPlong> mtpOrder;
mtpOrder.reserve(_stickersOrder.size());
for (const auto setId : std::as_const(_stickersOrder)) {
mtpOrder.push_back(MTP_long(setId));
}
_stickersReorderRequestId = request(MTPmessages_ReorderStickerSets(
MTP_flags(0),
MTP_vector<MTPlong>(mtpOrder)
)).done([=](const MTPBool &result) {
_stickersReorderRequestId = 0;
}).fail([=](const MTP::Error &error) {
_stickersReorderRequestId = 0;
_session->data().stickers().setLastUpdate(0);
updateStickers();
}).send();
}
void ApiWrap::updateStickers() {
auto now = crl::now();
const auto now = crl::now();
requestStickers(now);
requestRecentStickers(now);
requestFavedStickers(now);
@@ -2917,8 +3017,14 @@ void ApiWrap::updateStickers() {
requestSavedGifs(now);
}
void ApiWrap::requestRecentStickersForce() {
requestRecentStickersWithHash(0);
void ApiWrap::updateMasks() {
const auto now = crl::now();
requestStickers(now, true);
requestRecentStickers(now, true);
}
void ApiWrap::requestRecentStickersForce(bool attached) {
requestRecentStickersWithHash(0, attached);
}
void ApiWrap::setGroupStickerSet(not_null<ChannelData*> megagroup, const MTPInputStickerSet &set) {
@@ -2977,17 +3083,29 @@ std::vector<not_null<DocumentData*>> *ApiWrap::stickersByEmoji(
return nullptr;
}
void ApiWrap::requestStickers(TimeId now) {
if (!_session->data().stickers().updateNeeded(now)
|| _stickersUpdateRequest) {
void ApiWrap::requestStickers(TimeId now, bool masks) {
const auto requestId = [=]() -> mtpRequestId & {
return masks
? _masksUpdateRequest
: _stickersUpdateRequest;
};
const auto needed = masks
? _session->data().stickers().masksUpdateNeeded(now)
: _session->data().stickers().updateNeeded(now);
if (!needed || requestId()) {
return;
}
auto onDone = [this](const MTPmessages_AllStickers &result) {
_session->data().stickers().setLastUpdate(crl::now());
_stickersUpdateRequest = 0;
const auto onDone = [=](const MTPmessages_AllStickers &result) {
if (masks) {
_session->data().stickers().setLastMasksUpdate(crl::now());
} else {
_session->data().stickers().setLastUpdate(crl::now());
}
requestId() = 0;
switch (result.type()) {
case mtpc_messages_allStickersNotModified: return;
case mtpc_messages_getMaskStickers:
case mtpc_messages_allStickers: {
auto &d = result.c_messages_allStickers();
_session->data().stickers().setsReceived(
@@ -2997,39 +3115,67 @@ void ApiWrap::requestStickers(TimeId now) {
default: Unexpected("Type in ApiWrap::stickersDone()");
}
};
_stickersUpdateRequest = request(MTPmessages_GetAllStickers(
MTP_int(Api::CountStickersHash(_session, true))
)).done(onDone).fail([=](const MTP::Error &error) {
LOG(("App Fail: Failed to get stickers!"));
const auto onFail = [=](const MTP::Error &error) {
LOG(("App Fail: Failed to get %1!"
).arg(masks ? "masks" : "stickers"));
onDone(MTP_messages_allStickersNotModified());
}).send();
};
auto hash = MTP_int(Api::CountStickersHash(_session, true));
requestId() = masks
? request(MTPmessages_GetMaskStickers(
std::move(hash))
).done(onDone).fail(onFail).send()
: request(MTPmessages_GetAllStickers(
std::move(hash))
).done(onDone).fail(onFail).send();
}
void ApiWrap::requestRecentStickers(TimeId now) {
if (!_session->data().stickers().recentUpdateNeeded(now)) {
void ApiWrap::requestRecentStickers(TimeId now, bool attached) {
const auto needed = attached
? _session->data().stickers().recentAttachedUpdateNeeded(now)
: _session->data().stickers().recentUpdateNeeded(now);
if (!needed) {
return;
}
requestRecentStickersWithHash(
Api::CountRecentStickersHash(_session));
Api::CountRecentStickersHash(_session, attached), attached);
}
void ApiWrap::requestRecentStickersWithHash(int32 hash) {
if (_recentStickersUpdateRequest) {
void ApiWrap::requestRecentStickersWithHash(int32 hash, bool attached) {
const auto requestId = [=]() -> mtpRequestId & {
return attached
? _recentAttachedStickersUpdateRequest
: _recentStickersUpdateRequest;
};
if (requestId()) {
return;
}
_recentStickersUpdateRequest = request(MTPmessages_GetRecentStickers(
MTP_flags(0),
const auto finish = [=] {
auto &stickers = _session->data().stickers();
if (attached) {
stickers.setLastRecentAttachedUpdate(crl::now());
} else {
stickers.setLastRecentUpdate(crl::now());
}
requestId() = 0;
};
const auto flags = attached
? MTPmessages_getRecentStickers::Flag::f_attached
: MTPmessages_getRecentStickers::Flags(0);
requestId() = request(MTPmessages_GetRecentStickers(
MTP_flags(flags),
MTP_int(hash)
)).done([=](const MTPmessages_RecentStickers &result) {
_session->data().stickers().setLastRecentUpdate(crl::now());
_recentStickersUpdateRequest = 0;
finish();
switch (result.type()) {
case mtpc_messages_recentStickersNotModified: return;
case mtpc_messages_recentStickers: {
auto &d = result.c_messages_recentStickers();
_session->data().stickers().specialSetReceived(
Data::Stickers::CloudRecentSetId,
attached
? Data::Stickers::CloudRecentAttachedSetId
: Data::Stickers::CloudRecentSetId,
tr::lng_recent_stickers(tr::now),
d.vstickers().v,
d.vhash().v,
@@ -3039,8 +3185,7 @@ void ApiWrap::requestRecentStickersWithHash(int32 hash) {
default: Unexpected("Type in ApiWrap::recentStickersDone()");
}
}).fail([=](const MTP::Error &error) {
_session->data().stickers().setLastRecentUpdate(crl::now());
_recentStickersUpdateRequest = 0;
finish();
LOG(("App Fail: Failed to get recent stickers!"));
}).send();
@@ -3947,9 +4092,12 @@ void ApiWrap::sendFile(
void ApiWrap::sendUploadedPhoto(
FullMsgId localId,
const MTPInputFile &file,
Api::SendOptions options) {
Api::SendOptions options,
std::vector<MTPInputDocument> attachedStickers) {
if (const auto item = _session->data().message(localId)) {
const auto media = Api::PrepareUploadedPhoto(file);
const auto media = Api::PrepareUploadedPhoto(
file,
std::move(attachedStickers));
if (const auto groupId = item->groupId()) {
uploadAlbumMedia(item, groupId, media);
} else {
@@ -3962,12 +4110,17 @@ void ApiWrap::sendUploadedDocument(
FullMsgId localId,
const MTPInputFile &file,
const std::optional<MTPInputFile> &thumb,
Api::SendOptions options) {
Api::SendOptions options,
std::vector<MTPInputDocument> attachedStickers) {
if (const auto item = _session->data().message(localId)) {
if (!item->media() || !item->media()->document()) {
return;
}
const auto media = Api::PrepareUploadedDocument(item, file, thumb);
const auto media = Api::PrepareUploadedDocument(
item,
file,
thumb,
std::move(attachedStickers));
const auto groupId = item->groupId();
if (groupId) {
uploadAlbumMedia(item, groupId, media);
@@ -4368,6 +4521,8 @@ void ApiWrap::sendMediaWithRandomId(
caption.entities,
Api::ConvertOption::SkipLocal);
const auto updateRecentStickers = Api::HasAttachedStickers(media);
const auto flags = MTPmessages_SendMedia::Flags(0)
| (replyTo
? MTPmessages_SendMedia::Flag::f_reply_to_msg_id
@@ -4400,6 +4555,10 @@ void ApiWrap::sendMediaWithRandomId(
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
finish();
if (updateRecentStickers) {
requestRecentStickersForce(true);
}
}).fail([=](const MTP::Error &error) {
sendMessageFail(error, peer, randomId, itemId);
finish();

View File

@@ -280,9 +280,11 @@ public:
void requestStickerSets();
void saveStickerSets(
const Data::StickersSetsOrder &localOrder,
const Data::StickersSetsOrder &localRemoved);
const Data::StickersSetsOrder &localRemoved,
bool setsMasks);
void updateStickers();
void requestRecentStickersForce();
void updateMasks();
void requestRecentStickersForce(bool attached = false);
void setGroupStickerSet(
not_null<ChannelData*> megagroup,
const MTPInputStickerSet &set);
@@ -339,12 +341,6 @@ public:
not_null<UserData*> user,
PhotoId afterId);
void stickerSetInstalled(uint64 setId) {
_stickerSetInstalled.fire_copy(setId);
}
auto stickerSetInstalled() const {
return _stickerSetInstalled.events();
}
void readFeaturedSetDelayed(uint64 setId);
void parseChannelParticipants(
@@ -410,12 +406,14 @@ public:
void sendUploadedPhoto(
FullMsgId localId,
const MTPInputFile &file,
Api::SendOptions options);
Api::SendOptions options,
std::vector<MTPInputDocument> attachedStickers);
void sendUploadedDocument(
FullMsgId localId,
const MTPInputFile &file,
const std::optional<MTPInputFile> &thumb,
Api::SendOptions options);
Api::SendOptions options,
std::vector<MTPInputDocument> attachedStickers);
void cancelLocalItem(not_null<HistoryItem*> item);
@@ -542,12 +540,9 @@ private:
mtpRequestId req);
void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result);
void stickerSetDisenabled(mtpRequestId requestId);
void stickersSaveOrder();
void requestStickers(TimeId now);
void requestRecentStickers(TimeId now);
void requestRecentStickersWithHash(int32 hash);
void requestStickers(TimeId now, bool masks = false);
void requestRecentStickers(TimeId now, bool attached = false);
void requestRecentStickersWithHash(int32 hash, bool attached = false);
void requestFavedStickers(TimeId now);
void requestFeaturedStickers(TimeId now);
void requestSavedGifs(TimeId now);
@@ -685,12 +680,16 @@ private:
base::Timer _draftsSaveTimer;
base::flat_set<mtpRequestId> _stickerSetDisenableRequests;
Data::StickersSetsOrder _stickersOrder;
base::flat_set<mtpRequestId> _maskSetDisenableRequests;
mtpRequestId _masksReorderRequestId = 0;
mtpRequestId _stickersReorderRequestId = 0;
mtpRequestId _stickersClearRecentRequestId = 0;
mtpRequestId _stickersClearRecentAttachedRequestId = 0;
mtpRequestId _stickersUpdateRequest = 0;
mtpRequestId _masksUpdateRequest = 0;
mtpRequestId _recentStickersUpdateRequest = 0;
mtpRequestId _recentAttachedStickersUpdateRequest = 0;
mtpRequestId _favedStickersUpdateRequest = 0;
mtpRequestId _featuredStickersUpdateRequest = 0;
mtpRequestId _savedGifsUpdateRequest = 0;
@@ -731,8 +730,6 @@ private:
base::Observable<PeerData*> _fullPeerUpdated;
rpl::event_stream<uint64> _stickerSetInstalled;
mtpRequestId _topPromotionRequestId = 0;
std::pair<QString, uint32> _topPromotionKey;
TimeId _topPromotionNextRequestTime = TimeId(0);

View File

@@ -282,8 +282,4 @@ namespace App {
return result;
}
QPixmap pixmapFromImageInPlace(QImage &&image) {
return QPixmap::fromImage(std::move(image), Qt::ColorOnly);
}
}

View File

@@ -45,6 +45,5 @@ namespace App {
constexpr auto kImageSizeLimit = 64 * 1024 * 1024; // Open images up to 64mb jpg/png/gif
QImage readImage(QByteArray data, QByteArray *format = nullptr, bool opaque = true, bool *animated = nullptr);
QImage readImage(const QString &file, QByteArray *format = nullptr, bool opaque = true, bool *animated = nullptr, QByteArray *content = 0);
QPixmap pixmapFromImageInPlace(QImage &&image);
};

View File

@@ -13,7 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/openssl_help.h"
#include "boxes/confirm_box.h"
#include "boxes/confirm_phone_box.h" // ExtractPhonePrefix.
#include "boxes/photo_crop_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/peers/add_participants_box.h"
#include "boxes/peers/edit_participant_box.h"
@@ -466,6 +465,7 @@ void GroupInfoBox::prepare() {
_photo.create(
this,
&_navigation->parentController()->window(),
((_type == Type::Channel)
? tr::lng_create_channel_crop
: tr::lng_create_group_crop)(tr::now),

View File

@@ -21,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/background_preview_box.h"
#include "boxes/confirm_box.h"
#include "window/window_session_controller.h"
#include "app.h"
#include "styles/style_overview.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -337,7 +336,7 @@ void BackgroundBox::Inner::validatePaperThumbnail(
Data::PatternColor(color),
paper.data.patternIntensity());
}
paper.thumbnail = App::pixmapFromImageInPlace(TakeMiddleSample(
paper.thumbnail = Ui::PixmapFromImage(TakeMiddleSample(
original,
st::backgroundSize));
paper.thumbnail.setDevicePixelRatio(cRetinaFactor());

View File

@@ -29,7 +29,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/confirm_box.h"
#include "boxes/background_preview_box.h"
#include "window/window_session_controller.h"
#include "app.h"
#include "styles/style_chat.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -399,7 +398,7 @@ BackgroundPreviewBox::BackgroundPreviewBox(
QWidget*,
not_null<Window::SessionController*> controller,
const Data::WallPaper &paper)
: SimpleElementDelegate(controller)
: SimpleElementDelegate(controller, [=] { update(); })
, _controller(controller)
, _text1(GenerateTextItem(
delegate(),
@@ -688,8 +687,8 @@ void BackgroundPreviewBox::setScaledFromImage(
if (!_full.isNull()) {
startFadeInFrom(std::move(_scaled));
}
_scaled = App::pixmapFromImageInPlace(std::move(image));
_blurred = App::pixmapFromImageInPlace(std::move(blurred));
_scaled = Ui::PixmapFromImage(std::move(image));
_blurred = Ui::PixmapFromImage(std::move(blurred));
if (_blur && (!_paper.document() || !_full.isNull())) {
_blur->setDisabled(false);
}

View File

@@ -76,10 +76,6 @@ defaultFeedUserpicButton: FeedUserpicButton {
innerPart: defaultUserpicButton;
}
cropPointSize: 10px;
cropSkip: 13px;
cropMinSize: 20px;
confirmInviteTitle: FlatLabel(defaultFlatLabel) {
align: align(center);
minWidth: 320px;
@@ -386,6 +382,7 @@ confirmCaptionArea: InputField(defaultInputField) {
textMargins: margins(1px, 26px, 31px, 4px);
heightMax: 158px;
}
confirmEditCaptionAreaHeightMax: 78px;
confirmBg: windowBgOver;
confirmMaxHeight: 245px;
confirmMaxHeightSkip: 25px;
@@ -427,33 +424,42 @@ backgroundScroll: ScrollArea(boxScroll) {
deltab: 10px;
}
editMediaButtonSize: 28px;
editMediaButtonSkip: 8px;
sendMediaPreviewSize: 308px;
sendMediaPreviewHeightMax: 1280;
sendMediaRowSkip: 10px;
editMediaButtonSize: 32px;
editMediaButtonSkip: 5px;
editMediaButtonFileSkipRight: 1px;
editMediaButtonFileSkipTop: 7px;
editMediaButtonIconFile: icon {{ "settings_edit", menuIconFg }};
editMediaButtonIconPhoto: icon {{ "settings_edit", roundedFg }};
editMediaButton: IconButton {
editMediaButtonIconFile: icon {{ "send_media/send_media_replace", menuIconFg }};
editMediaButton: IconButton(defaultIconButton) {
width: editMediaButtonSize;
height: editMediaButtonSize;
icon: editMediaButtonIconPhoto;
icon: editMediaButtonIconFile;
rippleAreaSize: editMediaButtonSize;
ripple: defaultRippleAnimation;
}
editMediaHintLabel: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg;
minWidth: sendMediaPreviewSize;
}
// SendFilesBox
sendBoxAlbumGroupEditInternalSkip: 8px;
sendBoxAlbumGroupSkipRight: 6px;
sendBoxAlbumGroupSkipTop: 6px;
sendBoxAlbumGroupRadius: 13px;
sendBoxAlbumGroupHeight: 26px;
sendBoxAlbumGroupSkipRight: 5px;
sendBoxAlbumGroupSkipTop: 5px;
sendBoxAlbumGroupRadius: 4px;
sendBoxAlbumGroupSize: size(62px, 25px);
sendBoxAlbumSmallGroupSize: size(30px, 25px);
sendBoxFileGroupSkipTop: 2px;
sendBoxFileGroupSkipRight: 8px;
sendBoxFileGroupSkipRight: 5px;
sendBoxFileGroupEditInternalSkip: -1px;
sendBoxAlbumGroupButtonFile: IconButton(editMediaButton) {
@@ -462,16 +468,11 @@ sendBoxAlbumGroupButtonFile: IconButton(editMediaButton) {
}
}
sendBoxAlbumGroupEditButtonIconFile: editMediaButtonIconFile;
sendBoxAlbumGroupDeleteButtonIconFile: icon {{ "history_file_cancel", menuIconFg, point(6px, 6px) }};
sendBoxAlbumGroupDeleteButtonIconFile: icon {{ "send_media/send_media_delete", menuIconFg }};
sendBoxAlbumGroupButtonMediaEdit: icon {{ "settings_edit", roundedFg, point(3px, -2px) }};
sendBoxAlbumGroupButtonMediaDelete: icon {{ "history_file_cancel", roundedFg, point(2px, 5px) }};
sendBoxAlbumGroupButtonMedia: IconButton {
width: sendBoxAlbumGroupHeight;
height: sendBoxAlbumGroupHeight;
icon: sendBoxAlbumGroupButtonMediaEdit;
}
sendBoxAlbumButtonMediaEdit: icon {{ "send_media/send_media_replace", roundedFg }};
sendBoxAlbumGroupButtonMediaEdit: icon {{ "send_media/send_media_replace", roundedFg, point(4px, 1px) }};
sendBoxAlbumGroupButtonMediaDelete: icon {{ "send_media/send_media_delete", roundedFg }};
// End of SendFilesBox
@@ -523,6 +524,7 @@ usernameTextStyle: TextStyle(boxTextStyle, passcodeTextStyle) {
}
usernameDefaultFg: windowSubTextFg;
editMediaLabelMargins: margins(0px, 11px, 0px, 0px);
editMediaCheckboxMargins: margins(0px, 15px, 23px, 15px);
downloadPathSkip: 10px;
@@ -649,10 +651,6 @@ groupStickersField: InputField(defaultMultiSelectSearchField) {
}
groupStickersSubTitleHeight: 36px;
sendMediaPreviewSize: 308px;
sendMediaPreviewHeightMax: 1280;
sendMediaRowSkip: 10px;
proxyUsePadding: margins(22px, 6px, 22px, 5px);
proxyTryIPv6Padding: margins(22px, 8px, 22px, 5px);
proxyRowPadding: margins(22px, 8px, 8px, 8px);

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_editing.h"
#include "api/api_text_entities.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/tabbed_panel.h"
@@ -50,20 +51,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/format_song_document_name.h"
#include "ui/text/format_values.h"
#include "ui/text/text_options.h"
#include "ui/chat/attach/attach_controls.h"
#include "ui/chat/attach/attach_prepare.h"
#include "ui/controls/emoji_button.h"
#include "ui/toast/toast.h"
#include "ui/cached_round_corners.h"
#include "ui/abstract_button.h"
#include "ui/ui_utility.h"
#include "window/window_session_controller.h"
#include "confirm_box.h"
#include "apiwrap.h"
#include "app.h" // App::pixmapFromImageInPlace.
#include "facades.h" // App::LambdaDelayed.
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_chat.h"
#include "editor/photo_editor_layer_widget.h"
#include <QtCore/QMimeData>
namespace {
@@ -204,7 +209,7 @@ EditCaptionBox::EditCaptionBox(
| Images::Option::RoundedTopRight
| Images::Option::RoundedBottomLeft
| Images::Option::RoundedBottomRight;
_thumb = App::pixmapFromImageInPlace(Images::prepare(
_thumb = Ui::PixmapFromImage(Images::prepare(
image->original(),
_thumbw * cIntRetinaFactor(),
0,
@@ -334,7 +339,7 @@ EditCaptionBox::EditCaptionBox(
const auto prepareBasicThumb = _refreshThumbnail;
const auto scaleThumbDown = [=] {
_thumb = App::pixmapFromImageInPlace(_thumb.toImage().scaled(
_thumb = Ui::PixmapFromImage(_thumb.toImage().scaled(
_thumbw * cIntRetinaFactor(),
_thumbh * cIntRetinaFactor(),
Qt::KeepAspectRatio,
@@ -381,6 +386,20 @@ EditCaptionBox::EditCaptionBox(
InitSpellchecker(_controller, _field);
auto label = object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
this,
object_ptr<Ui::FlatLabel>(
this,
tr::lng_edit_photo_editor_hint(tr::now),
st::editMediaHintLabel),
st::editMediaLabelMargins);
_hintLabel = label.data();
_hintLabel->toggle(
_controller->session().settings().photoEditorHintShown()
? _photo
: false,
anim::type::instant);
auto r = object_ptr<Ui::SlideWrap<Ui::Checkbox>>(
this,
object_ptr<Ui::Checkbox>(
@@ -402,6 +421,48 @@ EditCaptionBox::EditCaptionBox(
) | rpl::start_with_next([=] {
closeBox();
}, lifetime());
_photoEditorOpens.events(
) | rpl::start_with_next([=, controller = _controller] {
const auto previewWidth = st::sendMediaPreviewSize;
if (!_preparedList.files.empty()) {
Editor::OpenWithPreparedFile(
this,
controller,
&_preparedList.files.front(),
previewWidth,
[=] { updateEditPreview(); });
} else {
auto callback = [=](const Editor::PhotoModifications &mods) {
if (!mods) {
return;
}
auto copy = computeImage()->original();
_preparedList = Storage::PrepareMediaFromImage(
std::move(copy),
QByteArray(),
previewWidth);
using ImageInfo = Ui::PreparedFileInformation::Image;
auto &file = _preparedList.files.front();
const auto image = std::get_if<ImageInfo>(
&file.information->media);
image->modifications = mods;
Storage::UpdateImageDetails(file, previewWidth);
updateEditPreview();
};
const auto fileImage = std::make_shared<Image>(*computeImage());
controller->showLayer(
std::make_unique<Editor::LayerWidget>(
this,
&controller->window(),
fileImage,
Editor::PhotoModifications(),
std::move(callback)),
Ui::LayerOption::KeepOther);
}
}, lifetime());
}
EditCaptionBox::~EditCaptionBox() = default;
@@ -594,9 +655,13 @@ void EditCaptionBox::updateEditPreview() {
const auto showCheckbox = _photo && (_albumType == Ui::AlbumType::None);
_wayWrap->toggle(showCheckbox, anim::type::instant);
if (_controller->session().settings().photoEditorHintShown()) {
_hintLabel->toggle(_photo, anim::type::instant);
}
_photoEditorButton->setVisible(_photo);
if (!_doc) {
_thumb = App::pixmapFromImageInPlace(
_thumb = Ui::PixmapFromImage(
file->preview.scaled(
st::sendMediaPreviewSize * cIntRetinaFactor(),
(st::confirmMaxHeight - (showCheckbox
@@ -614,17 +679,13 @@ void EditCaptionBox::updateEditPreview() {
}
}
updateEditMediaButton();
updateCaptionMaxHeight();
captionResized();
}
void EditCaptionBox::updateEditMediaButton() {
const auto icon = _doc
? &st::editMediaButtonIconFile
: &st::editMediaButtonIconPhoto;
const auto color = _doc ? &st::windowBgRipple : &st::roundedBg;
_editMedia->setIconOverride(icon);
_editMedia->setRippleColorOverride(color);
_editMedia->setForceRippled(!_doc, anim::type::instant);
_editMedia->setVisible(!_doc);
_editFile->setVisible(_doc);
}
void EditCaptionBox::createEditMediaButton() {
@@ -677,13 +738,26 @@ void EditCaptionBox::createEditMediaButton() {
lifetime());
// Create edit media button.
_editMedia.create(this, st::editMediaButton);
_editMedia.create(this, Ui::AttachControls::Type::EditOnly);
_editFile.create(this, st::editMediaButton);
updateEditMediaButton();
_editMedia->setClickedCallback(
_editFile->setClickedCallback(
App::LambdaDelayed(
st::historyAttach.ripple.hideDuration,
this,
buttonCallback));
_editMedia->editRequests(
) | rpl::start_with_next(buttonCallback, _editMedia->lifetime());
_photoEditorButton = base::make_unique_q<Ui::AbstractButton>(this);
_photoEditorButton->clicks(
) | rpl::to_empty | rpl::start_to_stream(
_photoEditorOpens,
_photoEditorButton->lifetime());
_photoEditorButton->raise();
_editMedia->raise();
}
void EditCaptionBox::prepare() {
@@ -728,6 +802,7 @@ void EditCaptionBox::prepare() {
auto cursor = _field->textCursor();
cursor.movePosition(QTextCursor::End);
_field->setTextCursor(cursor);
updateCaptionMaxHeight();
setupDragArea();
}
@@ -771,6 +846,28 @@ void EditCaptionBox::captionResized() {
update();
}
void EditCaptionBox::updateCaptionMaxHeight() {
// Save.
const auto wasCursor = _field->textCursor();
const auto position = wasCursor.position();
const auto anchor = wasCursor.anchor();
const auto text = _field->getTextWithAppliedMarkdown();
_field->setTextWithTags({});
_field->setMaxHeight(_doc
? st::confirmCaptionArea.heightMax
: (st::confirmEditCaptionAreaHeightMax - (_hintLabel->height() / 2)));
// Restore.
_field->setTextWithTags(text);
auto cursor = _field->textCursor();
cursor.setPosition(anchor);
if (position != anchor) {
cursor.setPosition(position, QTextCursor::KeepAnchor);
}
_field->setTextCursor(cursor);
}
void EditCaptionBox::setupEmojiPanel() {
const auto container = getDelegate()->outerContainer();
_emojiPanel = base::make_unique_q<ChatHelpers::TabbedPanel>(
@@ -843,6 +940,9 @@ void EditCaptionBox::updateBoxSize() {
if (_photo) {
newHeight += _wayWrap->height() / 2;
}
if (_hintLabel->toggled()) {
newHeight += _hintLabel->height();
}
const auto &st = isThumbedLayout()
? st::msgFileThumbLayout
: st::msgFileLayout;
@@ -887,39 +987,56 @@ void EditCaptionBox::startStreamedPlayer() {
void EditCaptionBox::paintEvent(QPaintEvent *e) {
BoxContent::paintEvent(e);
const auto &padding = st::boxPhotoPadding;
Painter p(this);
if (_photo || _animated) {
const auto th = std::max(_gifh, _thumbh);
if (_thumbx > st::boxPhotoPadding.left()) {
p.fillRect(st::boxPhotoPadding.left(), st::boxPhotoPadding.top(), _thumbx - st::boxPhotoPadding.left(), th, st::confirmBg);
if (_thumbx > padding.left()) {
p.fillRect(
padding.left(),
padding.top(),
_thumbx - padding.left(),
th,
st::confirmBg);
}
if (_thumbx + _thumbw < width() - st::boxPhotoPadding.right()) {
p.fillRect(_thumbx + _thumbw, st::boxPhotoPadding.top(), width() - st::boxPhotoPadding.right() - _thumbx - _thumbw, th, st::confirmBg);
if (_thumbx + _thumbw < width() - padding.right()) {
p.fillRect(
_thumbx + _thumbw,
padding.top(),
width() - padding.right() - _thumbx - _thumbw,
th,
st::confirmBg);
}
checkStreamedIsStarted();
if (_streamed
&& _streamed->player().ready()
&& !_streamed->player().videoSize().isEmpty()) {
const auto s = QSize(_gifw, _gifh);
const auto paused = _controller->isGifPausedAtLeastFor(Window::GifPauseReason::Layer);
const auto paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer);
auto request = ::Media::Streaming::FrameRequest();
request.outer = s * cIntRetinaFactor();
request.resize = s * cIntRetinaFactor();
p.drawImage(
QRect(_gifx, st::boxPhotoPadding.top(), _gifw, _gifh),
QRect(_gifx, padding.top(), _gifw, _gifh),
_streamed->frame(request));
if (!paused) {
_streamed->markFrameShown();
}
} else {
const auto offset = _gifh ? ((_gifh - _thumbh) / 2) : 0;
p.drawPixmap(_thumbx, st::boxPhotoPadding.top() + offset, _thumb);
p.drawPixmap(_thumbx, padding.top() + offset, _thumb);
}
if (_animated && !_streamed) {
const auto &st = st::msgFileLayout;
QRect inner(_thumbx + (_thumbw - st.thumbSize) / 2, st::boxPhotoPadding.top() + (th - st.thumbSize) / 2, st.thumbSize, st.thumbSize);
QRect inner(
_thumbx + (_thumbw - st.thumbSize) / 2,
padding.top() + (th - st.thumbSize) / 2,
st.thumbSize,
st.thumbSize);
p.setPen(Qt::NoPen);
p.setBrush(st::msgDateImgBg);
@@ -935,21 +1052,26 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
const auto &st = isThumbedLayout()
? st::msgFileThumbLayout
: st::msgFileLayout;
const auto w = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right();
const auto w = width() - padding.left() - padding.right();
const auto h = 0 + st.thumbSize + 0;
const auto nameleft = 0 + st.thumbSize + st.padding.right();
const auto nametop = st.nameTop - st.padding.top();
const auto nameright = 0;
const auto statustop = st.statusTop - st.padding.top();
const auto editButton = _isAllowedEditMedia
? _editMedia->width() + st::editMediaButtonSkip
? _editFile->width() + st::editMediaButtonSkip
: 0;
const auto namewidth = w - nameleft - editButton;
const auto x = (width() - w) / 2, y = st::boxPhotoPadding.top();
const auto x = (width() - w) / 2, y = padding.top();
// Ui::FillRoundCorner(p, x, y, w, h, st::msgInBg, Ui::MessageInCorners, &st::msgInShadow);
const auto rthumb = style::rtlrect(x + 0, y + 0, st.thumbSize, st.thumbSize, width());
const auto rthumb = style::rtlrect(
x + 0,
y + 0,
st.thumbSize,
st.thumbSize,
width());
if (isThumbedLayout()) {
p.drawPixmap(rthumb.topLeft(), _thumb);
} else {
@@ -972,7 +1094,12 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
}
p.setFont(st::semiboldFont);
p.setPen(st::historyFileNameInFg);
_name.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width());
_name.drawLeftElided(
p,
x + nameleft,
y + nametop,
namewidth,
width());
const auto &status = st::mediaInFg;
p.setFont(st::normalFont);
@@ -981,38 +1108,67 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
} else {
p.setFont(st::boxTitleFont);
p.setPen(st::boxTextFg);
p.drawTextLeft(_field->x(), st::boxPhotoPadding.top(), width(), tr::lng_edit_message(tr::now));
p.drawTextLeft(
_field->x(),
padding.top(),
width(),
tr::lng_edit_message(tr::now));
}
if (!_error.isEmpty()) {
p.setFont(st::normalFont);
p.setPen(st::boxTextFgError);
p.drawTextLeft(_field->x(), _field->y() + _field->height() + errorTopSkip(), width(), _error);
p.drawTextLeft(
_field->x(),
_field->y() + _field->height() + errorTopSkip(),
width(),
_error);
}
if (_isAllowedEditMedia) {
_editMedia->moveToRight(
st::boxPhotoPadding.right() + (_doc
? st::editMediaButtonFileSkipRight
: st::editMediaButtonSkip),
st::boxPhotoPadding.top() + (_doc
? st::editMediaButtonFileSkipTop
: st::editMediaButtonSkip));
if (_doc) {
_editFile->moveToRight(
padding.right() + st::editMediaButtonFileSkipRight,
padding.top() + st::editMediaButtonFileSkipTop);
} else {
_editMedia->moveToRight(
padding.right() + st::editMediaButtonSkip,
padding.top() + st::editMediaButtonSkip);
}
}
}
void EditCaptionBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
const auto previewBottom = st::boxPhotoPadding.top() + _thumbh;
const auto hintToggled = _hintLabel->toggled();
if (hintToggled) {
_hintLabel->resize(st::sendMediaPreviewSize, _hintLabel->height());
_hintLabel->moveToLeft(st::boxPhotoPadding.left(), previewBottom);
}
if (_photo) {
_wayWrap->resize(st::sendMediaPreviewSize, _wayWrap->height());
_wayWrap->moveToLeft(
st::boxPhotoPadding.left(),
st::boxPhotoPadding.top() + _thumbh);
hintToggled
? _hintLabel->y() + _hintLabel->height()
: previewBottom);
_photoEditorButton->resize(_thumbw, _thumbh);
_photoEditorButton->moveToLeft(_thumbx, st::boxPhotoPadding.top());
}
_field->resize(st::sendMediaPreviewSize, _field->height());
_field->moveToLeft(st::boxPhotoPadding.left(), height() - st::normalFont->height - errorTopSkip() - _field->height());
_field->moveToLeft(
st::boxPhotoPadding.left(),
height()
- st::normalFont->height
- errorTopSkip()
- _field->height());
_emojiToggle->moveToLeft(
(st::boxPhotoPadding.left()
+ st::sendMediaPreviewSize
@@ -1048,6 +1204,11 @@ void EditCaptionBox::save() {
action.options = options;
action.replaceMediaOf = item->fullId().msg;
if (Storage::ApplyModifications(_preparedList)) {
_controller->session().settings().incrementPhotoEditorHintShown();
_controller->session().saveSettings();
}
_controller->session().api().editMedia(
std::move(_preparedList),
(!_asFile && _photo) ? SendMediaType::Photo : SendMediaType::File,
@@ -1099,8 +1260,10 @@ void EditCaptionBox::setName(QString nameString, qint64 size) {
}
void EditCaptionBox::keyPressEvent(QKeyEvent *e) {
if ((e->key() == Qt::Key_E || e->key() == Qt::Key_O)
&& e->modifiers() == Qt::ControlModifier) {
const auto ctrl = e->modifiers().testFlag(Qt::ControlModifier);
if ((e->key() == Qt::Key_E) && ctrl) {
_photoEditorOpens.fire({});
} else if ((e->key() == Qt::Key_O) && ctrl) {
_editMediaClicks.fire({});
} else {
e->ignore();

View File

@@ -29,11 +29,13 @@ class DocumentMedia;
} // namespace Data
namespace Ui {
class AbstractButton;
class InputField;
class EmojiButton;
class IconButton;
class Checkbox;
enum class AlbumType;
class AttachControlsWidget;
} // namespace Ui
namespace Window {
@@ -90,6 +92,7 @@ private:
bool fileFromClipboard(not_null<const QMimeData*> data);
void updateEditPreview();
void updateEditMediaButton();
void updateCaptionMaxHeight();
int errorTopSkip() const;
@@ -122,6 +125,7 @@ private:
object_ptr<Ui::EmojiButton> _emojiToggle = { nullptr };
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
base::unique_qptr<QObject> _emojiFilter;
base::unique_qptr<Ui::AbstractButton> _photoEditorButton;
int _thumbx = 0;
int _thumbw = 0;
@@ -139,13 +143,16 @@ private:
mtpRequestId _saveRequestId = 0;
object_ptr<Ui::IconButton> _editMedia = nullptr;
object_ptr<Ui::AttachControlsWidget> _editMedia = nullptr;
object_ptr<Ui::IconButton> _editFile = nullptr;
Ui::SlideWrap<Ui::RpWidget> *_wayWrap = nullptr;
Ui::SlideWrap<Ui::RpWidget> *_hintLabel = nullptr;
QString _newMediaPath;
Ui::AlbumType _albumType = Ui::AlbumType();
bool _isAllowedEditMedia = false;
bool _asFile = false;
rpl::event_stream<> _editMediaClicks;
rpl::event_stream<> _photoEditorOpens;
QString _error;

View File

@@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/input_fields.h"
#include "ui/ui_utility.h"
#include "base/platform/base_platform_info.h"
#include "app.h"
#include "styles/style_boxes.h"
#include "styles/style_media_view.h"
@@ -423,7 +422,7 @@ void EditColorBox::Slider::generatePixmap() {
if (!isHorizontal()) {
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
}
_pixmap = App::pixmapFromImageInPlace(std::move(image));
_pixmap = Ui::PixmapFromImage(std::move(image));
} else if (_type == Type::Opacity) {
auto color = anim::shifted(QColor(255, 255, 255, 255));
auto transparent = anim::shifted(QColor(255, 255, 255, 0));
@@ -459,7 +458,7 @@ void EditColorBox::Slider::generatePixmap() {
if (!isHorizontal()) {
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
}
_pixmap = App::pixmapFromImageInPlace(std::move(image));
_pixmap = Ui::PixmapFromImage(std::move(image));
}
}
@@ -530,7 +529,7 @@ void EditColorBox::Slider::setLightnessLimits(int min, int max) {
}
void EditColorBox::Slider::updatePixmapFromMask() {
_pixmap = App::pixmapFromImageInPlace(style::colorizeImage(_mask, _color));
_pixmap = Ui::PixmapFromImage(style::colorizeImage(_mask, _color));
}
void EditColorBox::Slider::updateCurrentPoint(QPoint localPosition) {

View File

@@ -495,6 +495,7 @@ object_ptr<Ui::RpWidget> Controller::createPhotoEdit() {
_wrap,
object_ptr<Ui::UserpicButton>(
_wrap,
&_navigation->parentController()->window(),
_peer,
Ui::UserpicButton::Role::ChangePhoto,
st::defaultUserpicButton),

View File

@@ -1,270 +0,0 @@
/*
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 "boxes/photo_crop_box.h"
#include "lang/lang_keys.h"
#include "ui/widgets/buttons.h"
#include "ui/ui_utility.h"
#include "app.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
PhotoCropBox::PhotoCropBox(
QWidget*,
const QImage &img,
const QString &title)
: _title(title)
, _img(img) {
}
void PhotoCropBox::prepare() {
addButton(tr::lng_settings_save(), [this] { sendPhoto(); });
addButton(tr::lng_cancel(), [this] { closeBox(); });
int32 s = st::boxWideWidth - st::boxPhotoPadding.left() - st::boxPhotoPadding.right();
_thumb = App::pixmapFromImageInPlace(_img.scaled(s * cIntRetinaFactor(), s * cIntRetinaFactor(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
_thumb.setDevicePixelRatio(cRetinaFactor());
_mask = QImage(_thumb.size(), QImage::Format_ARGB32_Premultiplied);
_mask.setDevicePixelRatio(cRetinaFactor());
_fade = QImage(_thumb.size(), QImage::Format_ARGB32_Premultiplied);
_fade.setDevicePixelRatio(cRetinaFactor());
_thumbw = _thumb.width() / cIntRetinaFactor();
_thumbh = _thumb.height() / cIntRetinaFactor();
if (_thumbw > _thumbh) {
_cropw = _thumbh - 20;
} else {
_cropw = _thumbw - 20;
}
_cropx = (_thumbw - _cropw) / 2;
_cropy = (_thumbh - _cropw) / 2;
_thumbx = (st::boxWideWidth - _thumbw) / 2;
_thumby = st::boxPhotoPadding.top();
setMouseTracking(true);
setDimensions(st::boxWideWidth, st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom() + st::boxTextFont->height + st::cropSkip);
}
void PhotoCropBox::mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
_downState = mouseState(e->pos());
_fromposx = e->pos().x();
_fromposy = e->pos().y();
_fromcropx = _cropx;
_fromcropy = _cropy;
_fromcropw = _cropw;
}
return BoxContent::mousePressEvent(e);
}
int PhotoCropBox::mouseState(QPoint p) {
p -= QPoint(_thumbx, _thumby);
int32 delta = st::cropPointSize, mdelta(-delta / 2);
if (QRect(_cropx + mdelta, _cropy + mdelta, delta, delta).contains(p)) {
return 1;
} else if (QRect(_cropx + _cropw + mdelta, _cropy + mdelta, delta, delta).contains(p)) {
return 2;
} else if (QRect(_cropx + _cropw + mdelta, _cropy + _cropw + mdelta, delta, delta).contains(p)) {
return 3;
} else if (QRect(_cropx + mdelta, _cropy + _cropw + mdelta, delta, delta).contains(p)) {
return 4;
} else if (QRect(_cropx, _cropy, _cropw, _cropw).contains(p)) {
return 5;
}
return 0;
}
rpl::producer<QImage> PhotoCropBox::ready() const {
return _readyImages.events();
}
void PhotoCropBox::mouseReleaseEvent(QMouseEvent *e) {
if (_downState) {
_downState = 0;
mouseMoveEvent(e);
}
}
void PhotoCropBox::mouseMoveEvent(QMouseEvent *e) {
if (_downState && !(e->buttons() & Qt::LeftButton)) {
mouseReleaseEvent(e);
}
if (_downState) {
if (_downState == 1) {
int32 dx = e->pos().x() - _fromposx, dy = e->pos().y() - _fromposy, d = (dx < dy) ? dx : dy;
if (_fromcropx + d < 0) {
d = -_fromcropx;
}
if (_fromcropy + d < 0) {
d = -_fromcropy;
}
if (_fromcropw - d < st::cropMinSize) {
d = _fromcropw - st::cropMinSize;
}
if (_cropx != _fromcropx + d || _cropy != _fromcropy + d || _cropw != _fromcropw - d) {
_cropx = _fromcropx + d;
_cropy = _fromcropy + d;
_cropw = _fromcropw - d;
update();
}
} else if (_downState == 2) {
int32 dx = _fromposx - e->pos().x(), dy = e->pos().y() - _fromposy, d = (dx < dy) ? dx : dy;
if (_fromcropx + _fromcropw - d > _thumbw) {
d = _fromcropx + _fromcropw - _thumbw;
}
if (_fromcropy + d < 0) {
d = -_fromcropy;
}
if (_fromcropw - d < st::cropMinSize) {
d = _fromcropw - st::cropMinSize;
}
if (_cropy != _fromcropy + d || _cropw != _fromcropw - d) {
_cropy = _fromcropy + d;
_cropw = _fromcropw - d;
update();
}
} else if (_downState == 3) {
int32 dx = _fromposx - e->pos().x(), dy = _fromposy - e->pos().y(), d = (dx < dy) ? dx : dy;
if (_fromcropx + _fromcropw - d > _thumbw) {
d = _fromcropx + _fromcropw - _thumbw;
}
if (_fromcropy + _fromcropw - d > _thumbh) {
d = _fromcropy + _fromcropw - _thumbh;
}
if (_fromcropw - d < st::cropMinSize) {
d = _fromcropw - st::cropMinSize;
}
if (_cropw != _fromcropw - d) {
_cropw = _fromcropw - d;
update();
}
} else if (_downState == 4) {
int32 dx = e->pos().x() - _fromposx, dy = _fromposy - e->pos().y(), d = (dx < dy) ? dx : dy;
if (_fromcropx + d < 0) {
d = -_fromcropx;
}
if (_fromcropy + _fromcropw - d > _thumbh) {
d = _fromcropy + _fromcropw - _thumbh;
}
if (_fromcropw - d < st::cropMinSize) {
d = _fromcropw - st::cropMinSize;
}
if (_cropx != _fromcropx + d || _cropw != _fromcropw - d) {
_cropx = _fromcropx + d;
_cropw = _fromcropw - d;
update();
}
} else if (_downState == 5) {
int32 dx = e->pos().x() - _fromposx, dy = e->pos().y() - _fromposy;
if (_fromcropx + dx < 0) {
dx = -_fromcropx;
} else if (_fromcropx + _fromcropw + dx > _thumbw) {
dx = _thumbw - _fromcropx - _fromcropw;
}
if (_fromcropy + dy < 0) {
dy = -_fromcropy;
} else if (_fromcropy + _fromcropw + dy > _thumbh) {
dy = _thumbh - _fromcropy - _fromcropw;
}
if (_cropx != _fromcropx + dx || _cropy != _fromcropy + dy) {
_cropx = _fromcropx + dx;
_cropy = _fromcropy + dy;
update();
}
}
}
int32 cursorState = _downState ? _downState : mouseState(e->pos());
QCursor cur(style::cur_default);
if (cursorState == 1 || cursorState == 3) {
cur = style::cur_sizefdiag;
} else if (cursorState == 2 || cursorState == 4) {
cur = style::cur_sizebdiag;
} else if (cursorState == 5) {
cur = style::cur_sizeall;
}
setCursor(cur);
}
void PhotoCropBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
sendPhoto();
} else {
BoxContent::keyPressEvent(e);
}
}
void PhotoCropBox::paintEvent(QPaintEvent *e) {
BoxContent::paintEvent(e);
Painter p(this);
p.setFont(st::boxTextFont);
p.setPen(st::boxPhotoTextFg);
p.drawText(QRect(st::boxPhotoPadding.left(), st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom(), width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(), st::boxTextFont->height), _title, style::al_top);
p.translate(_thumbx, _thumby);
p.drawPixmap(0, 0, _thumb);
_mask.fill(Qt::white);
{
Painter p(&_mask);
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
p.setBrush(Qt::black);
p.drawEllipse(_cropx, _cropy, _cropw, _cropw);
}
style::colorizeImage(_mask, st::photoCropFadeBg->c, &_fade);
p.drawImage(0, 0, _fade);
int delta = st::cropPointSize;
int mdelta = -delta / 2;
p.fillRect(QRect(_cropx + mdelta, _cropy + mdelta, delta, delta), st::photoCropPointFg);
p.fillRect(QRect(_cropx + _cropw + mdelta, _cropy + mdelta, delta, delta), st::photoCropPointFg);
p.fillRect(QRect(_cropx + _cropw + mdelta, _cropy + _cropw + mdelta, delta, delta), st::photoCropPointFg);
p.fillRect(QRect(_cropx + mdelta, _cropy + _cropw + mdelta, delta, delta), st::photoCropPointFg);
}
void PhotoCropBox::sendPhoto() {
auto from = _img;
if (_img.width() < _thumb.width()) {
from = _thumb.toImage();
}
float64 x = float64(_cropx) / _thumbw, y = float64(_cropy) / _thumbh, w = float64(_cropw) / _thumbw;
int32 ix = int32(x * from.width()), iy = int32(y * from.height()), iw = int32(w * from.width());
if (ix < 0) {
ix = 0;
}
if (ix + iw > from.width()) {
iw = from.width() - ix;
}
if (iy < 0) {
iy = 0;
}
if (iy + iw > from.height()) {
iw = from.height() - iy;
}
int32 offset = ix * from.depth() / 8 + iy * from.bytesPerLine();
QImage cropped(from.constBits() + offset, iw, iw, from.bytesPerLine(), from.format()), tosend;
if (from.format() == QImage::Format_Indexed8) {
cropped.setColorCount(from.colorCount());
cropped.setColorTable(from.colorTable());
}
if (cropped.width() > 1280) {
tosend = cropped.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation);
} else if (cropped.width() < 320) {
tosend = cropped.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation);
} else {
tosend = cropped.copy();
}
auto weak = Ui::MakeWeak(this);
_readyImages.fire(std::move(tosend));
if (weak) {
closeBox();
}
}

View File

@@ -1,42 +0,0 @@
/*
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 "boxes/abstract_box.h"
class PhotoCropBox : public Ui::BoxContent {
public:
PhotoCropBox(QWidget*, const QImage &img, const QString &title);
int32 mouseState(QPoint p);
rpl::producer<QImage> ready() const;
protected:
void prepare() override;
void keyPressEvent(QKeyEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
private:
void sendPhoto();
QString _title;
int32 _downState = 0;
int32 _thumbx, _thumby, _thumbw, _thumbh;
int32 _cropx, _cropy, _cropw;
int32 _fromposx, _fromposy, _fromcropx, _fromcropy, _fromcropw;
QImage _img;
QPixmap _thumb;
QImage _mask, _fade;
rpl::event_stream<QImage> _readyImages;
};

View File

@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_media_prepare.h"
#include "mainwidget.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "mtproto/mtproto_config.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/send_context_menu.h"
@@ -20,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "confirm_box.h"
#include "editor/photo_editor_layer_widget.h"
#include "history/history_drag_area.h"
#include "history/view/history_view_schedule_box.h"
#include "core/file_utilities.h"
@@ -187,6 +189,22 @@ rpl::producer<int> SendFilesBox::Block::itemReplaceRequest() const {
}
}
rpl::producer<int> SendFilesBox::Block::itemModifyRequest() const {
using namespace rpl::mappers;
const auto preview = _preview.get();
const auto from = _from;
if (_isAlbum) {
const auto album = static_cast<Ui::AlbumPreview*>(preview);
return album->thumbModified() | rpl::map(_1 + from);
} else if (_isSingleMedia) {
const auto media = static_cast<Ui::SingleMediaPreview*>(preview);
return media->modifyRequests() | rpl::map_to(from);
} else {
return rpl::never<int>();
}
}
void SendFilesBox::Block::setSendWay(Ui::SendFilesWay way) {
if (!_isAlbum) {
return;
@@ -601,6 +619,16 @@ void SendFilesBox::pushBlock(int from, int till) {
FileDialog::AllOrImagesFilter(),
crl::guard(this, callback));
}, widget->lifetime());
block.itemModifyRequest(
) | rpl::start_with_next([=, controller = _controller](int index) {
Editor::OpenWithPreparedFile(
this,
controller,
&_list.files[index],
st::sendMediaPreviewSize,
[=] { refreshAllAfterChanges(from); });
}, widget->lifetime());
}
void SendFilesBox::refreshControls() {
@@ -640,6 +668,11 @@ void SendFilesBox::setupSendWayControls() {
sendWay.setSendImagesAsPhotos(_sendImagesAsPhotos->checked());
_sendWay = sendWay;
}, lifetime());
_hintLabel.create(
this,
tr::lng_edit_photo_editor_hint(tr::now),
st::editMediaHintLabel);
}
void SendFilesBox::updateSendWayControlsVisibility() {
@@ -647,6 +680,11 @@ void SendFilesBox::updateSendWayControlsVisibility() {
_groupFiles->setVisible(_list.hasGroupOption(onlyOne));
_sendImagesAsPhotos->setVisible(
_list.hasSendImagesAsPhotosOption(onlyOne));
_hintLabel->setVisible(
_controller->session().settings().photoEditorHintShown()
? _list.hasSendImagesAsPhotosOption(false)
: false);
}
void SendFilesBox::setupCaption() {
@@ -850,14 +888,15 @@ void SendFilesBox::updateBoxSize() {
if (_caption) {
footerHeight += st::boxPhotoCaptionSkip + _caption->height();
}
const auto pointers = {
_groupFiles.data(),
_sendImagesAsPhotos.data(),
};
for (auto pointer : pointers) {
const auto pairs = std::array<std::pair<RpWidget*, int>, 3>{ {
{ _groupFiles.data(), st::boxPhotoCompressedSkip },
{ _sendImagesAsPhotos.data(), st::boxPhotoCompressedSkip },
{ _hintLabel.data(), st::editMediaLabelMargins.top() },
} };
for (const auto &pair : pairs) {
const auto pointer = pair.first;
if (pointer && !pointer->isHidden()) {
footerHeight += st::boxPhotoCompressedSkip
+ pointer->heightNoMargins();
footerHeight += pair.second + pointer->heightNoMargins();
}
}
_footerHeight = footerHeight;
@@ -916,16 +955,18 @@ void SendFilesBox::updateControlsGeometry() {
_emojiToggle->update();
}
}
const auto pointers = {
_groupFiles.data(),
_sendImagesAsPhotos.data(),
};
for (const auto pointer : ranges::views::reverse(pointers)) {
const auto pairs = std::array<std::pair<RpWidget*, int>, 3>{ {
{ _hintLabel.data(), st::editMediaLabelMargins.top() },
{ _groupFiles.data(), st::boxPhotoCompressedSkip },
{ _sendImagesAsPhotos.data(), st::boxPhotoCompressedSkip },
} };
for (const auto &pair : ranges::views::reverse(pairs)) {
const auto pointer = pair.first;
if (pointer && !pointer->isHidden()) {
pointer->moveToLeft(
st::boxPhotoPadding.left(),
bottom - pointer->heightNoMargins());
bottom -= st::boxPhotoCompressedSkip + pointer->heightNoMargins();
bottom -= pair.second + pointer->heightNoMargins();
}
}
_scroll->resize(width(), bottom - _titleHeight.current());
@@ -976,6 +1017,12 @@ void SendFilesBox::send(
for (auto &block : _blocks) {
block.applyAlbumOrder();
}
if (Storage::ApplyModifications(_list)) {
_controller->session().settings().incrementPhotoEditorHintShown();
_controller->session().saveSettings();
}
_confirmed = true;
if (_confirmedCallback) {
auto caption = (_caption && !_caption->isHidden())

View File

@@ -35,6 +35,7 @@ struct GroupMediaLayout;
class EmojiButton;
class AlbumPreview;
class VerticalLayout;
class FlatLabel;
} // namespace Ui
namespace Window {
@@ -102,6 +103,7 @@ private:
[[nodiscard]] rpl::producer<int> itemDeleteRequest() const;
[[nodiscard]] rpl::producer<int> itemReplaceRequest() const;
[[nodiscard]] rpl::producer<int> itemModifyRequest() const;
void setSendWay(Ui::SendFilesWay way);
void applyAlbumOrder();
@@ -183,6 +185,7 @@ private:
object_ptr<Ui::Checkbox> _groupFiles = { nullptr };
object_ptr<Ui::Checkbox> _sendImagesAsPhotos = { nullptr };
object_ptr<Ui::FlatLabel> _hintLabel = { nullptr };
rpl::variable<Ui::SendFilesWay> _sendWay = Ui::SendFilesWay();
rpl::variable<int> _footerHeight = 0;

View File

@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h"
#include "ui/image/image_location_factory.h"
#include "ui/text/text_utilities.h"
#include "ui/effects/path_shift_gradient.h"
#include "ui/emoji_config.h"
#include "ui/toast/toast.h"
#include "ui/widgets/popup_menu.h"
@@ -68,10 +69,17 @@ public:
void install();
[[nodiscard]] rpl::producer<uint64> setInstalled() const;
[[nodiscard]] rpl::producer<uint64> setArchived() const;
[[nodiscard]] rpl::producer<> updateControls() const;
[[nodiscard]] rpl::producer<Error> errors() const;
void archiveStickers();
bool isMasksSet() const {
return (_setFlags & MTPDstickerSet::Flag::f_masks);
}
~Inner();
protected:
@@ -104,10 +112,6 @@ private:
void gotSet(const MTPmessages_StickerSet &set);
void installDone(const MTPmessages_StickerSetInstallResult &result);
bool isMasksSet() const {
return (_setFlags & MTPDstickerSet::Flag::f_masks);
}
not_null<Lottie::MultiPlayer*> getLottiePlayer();
void showPreview();
@@ -128,6 +132,8 @@ private:
TimeId _setInstallDate = TimeId(0);
ImageWithLocation _setThumbnail;
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
MTPInputStickerSet _input;
mtpRequestId _installRequest = 0;
@@ -138,6 +144,7 @@ private:
int _previewShown = -1;
rpl::event_stream<uint64> _setInstalled;
rpl::event_stream<uint64> _setArchived;
rpl::event_stream<> _updateControls;
rpl::event_stream<Error> _errors;
@@ -186,7 +193,12 @@ void StickerSetBox::prepare() {
_inner->setInstalled(
) | rpl::start_with_next([=](uint64 setId) {
_controller->session().api().stickerSetInstalled(setId);
if (_inner->isMasksSet()) {
Ui::Toast::Show(tr::lng_masks_installed(tr::now));
} else {
auto &stickers = _controller->session().data().stickers();
stickers.notifyStickerSetInstalled(setId);
}
closeBox();
}, lifetime());
@@ -194,6 +206,36 @@ void StickerSetBox::prepare() {
) | rpl::start_with_next([=](Error error) {
handleError(error);
}, lifetime());
_inner->setArchived(
) | rpl::start_with_next([=](uint64 setId) {
const auto isMasks = _inner->isMasksSet();
Ui::Toast::Show(isMasks
? tr::lng_masks_has_been_archived(tr::now)
: tr::lng_stickers_has_been_archived(tr::now));
auto &order = isMasks
? _controller->session().data().stickers().maskSetsOrderRef()
: _controller->session().data().stickers().setsOrderRef();
const auto index = order.indexOf(setId);
if (index != -1) {
order.removeAt(index);
auto &local = _controller->session().local();
if (isMasks) {
local.writeInstalledMasks();
local.writeArchivedMasks();
} else {
local.writeInstalledStickers();
local.writeArchivedStickers();
}
}
_controller->session().data().stickers().notifyUpdated();
closeBox();
}, lifetime());
}
void StickerSetBox::addStickers() {
@@ -220,38 +262,6 @@ void StickerSetBox::handleError(Error error) {
}
}
void StickerSetBox::archiveStickers() {
const auto weak = base::make_weak(_controller.get());
const auto setId = _set.c_inputStickerSetID().vid().v;
_controller->session().api().request(MTPmessages_InstallStickerSet(
_set,
MTP_boolTrue()
)).done([=](const MTPmessages_StickerSetInstallResult &result) {
const auto controller = weak.get();
if (!controller) {
return;
}
if (result.type() == mtpc_messages_stickerSetInstallResultSuccess) {
Ui::Toast::Show(tr::lng_stickers_has_been_archived(tr::now));
const auto &session = controller->session();
auto &order = session.data().stickers().setsOrderRef();
const auto index = order.indexOf(setId);
if (index == -1) {
return;
}
order.removeAt(index);
session.local().writeInstalledStickers();
session.local().writeArchivedStickers();
session.data().stickers().notifyUpdated();
}
}).fail([](const MTP::Error &error) {
Ui::Toast::Show(Lang::Hard::ServerError());
}).send();
}
void StickerSetBox::updateTitleAndButtons() {
setTitle(_inner->title());
updateButtons();
@@ -260,8 +270,12 @@ void StickerSetBox::updateTitleAndButtons() {
void StickerSetBox::updateButtons() {
clearButtons();
if (_inner->loaded()) {
const auto isMasks = _inner->isMasksSet();
if (_inner->notInstalled()) {
addButton(tr::lng_stickers_add_pack(), [=] { addStickers(); });
auto addText = isMasks
? tr::lng_stickers_add_masks()
: tr::lng_stickers_add_pack();
addButton(std::move(addText), [=] { addStickers(); });
addButton(tr::lng_cancel(), [=] { closeBox(); });
if (!_inner->shortName().isEmpty()) {
@@ -276,7 +290,9 @@ void StickerSetBox::updateButtons() {
top->setClickedCallback([=] {
*menu = base::make_unique_q<Ui::PopupMenu>(top);
(*menu)->addAction(
tr::lng_stickers_share_pack(tr::now),
(isMasks
? tr::lng_stickers_share_masks
: tr::lng_stickers_share_pack)(tr::now),
share);
(*menu)->popup(QCursor::pos());
return true;
@@ -289,21 +305,25 @@ void StickerSetBox::updateButtons() {
copyStickersLink();
Ui::Toast::Show(tr::lng_stickers_copied(tr::now));
};
addButton(tr::lng_stickers_share_pack(), std::move(share));
auto shareText = isMasks
? tr::lng_stickers_share_masks()
: tr::lng_stickers_share_pack();
addButton(std::move(shareText), std::move(share));
addButton(tr::lng_cancel(), [=] { closeBox(); });
if (!_inner->shortName().isEmpty()) {
const auto top = addTopButton(st::infoTopBarMenu);
const auto archive = [=] {
archiveStickers();
closeBox();
_inner->archiveStickers();
};
const auto menu =
std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
top->setClickedCallback([=] {
*menu = base::make_unique_q<Ui::PopupMenu>(top);
(*menu)->addAction(
tr::lng_stickers_archive_pack(tr::now),
isMasks
? tr::lng_masks_archive_pack(tr::now)
: tr::lng_stickers_archive_pack(tr::now),
archive);
(*menu)->popup(QCursor::pos());
return true;
@@ -328,6 +348,10 @@ StickerSetBox::Inner::Inner(
: RpWidget(parent)
, _controller(controller)
, _api(&_controller->session().mtp())
, _pathGradient(std::make_unique<Ui::PathShiftGradient>(
st::windowBgRipple,
st::windowBgOver,
[=] { update(); }))
, _input(set)
, _previewTimer([=] { showPreview(); }) {
set.match([&](const MTPDinputStickerSetID &data) {
@@ -457,6 +481,10 @@ rpl::producer<uint64> StickerSetBox::Inner::setInstalled() const {
return _setInstalled.events();
}
rpl::producer<uint64> StickerSetBox::Inner::setArchived() const {
return _setArchived.events();
}
rpl::producer<> StickerSetBox::Inner::updateControls() const {
return _updateControls.events();
}
@@ -467,13 +495,19 @@ rpl::producer<StickerSetBox::Error> StickerSetBox::Inner::errors() const {
void StickerSetBox::Inner::installDone(
const MTPmessages_StickerSetInstallResult &result) {
auto &sets = _controller->session().data().stickers().setsRef();
auto &stickers = _controller->session().data().stickers();
auto &sets = stickers.setsRef();
const auto isMasks = isMasksSet();
bool wasArchived = (_setFlags & MTPDstickerSet::Flag::f_archived);
const bool wasArchived = (_setFlags & MTPDstickerSet::Flag::f_archived);
if (wasArchived) {
auto index = _controller->session().data().stickers().archivedSetsOrderRef().indexOf(_setId);
const auto index = (isMasks
? stickers.archivedMaskSetsOrderRef()
: stickers.archivedSetsOrderRef()).indexOf(_setId);
if (index >= 0) {
_controller->session().data().stickers().archivedSetsOrderRef().removeAt(index);
(isMasks
? stickers.archivedMaskSetsOrderRef()
: stickers.archivedSetsOrderRef()).removeAt(index);
}
}
_setInstallDate = base::unixtime::now();
@@ -502,8 +536,10 @@ void StickerSetBox::Inner::installDone(
set->stickers = _pack;
set->emoji = _emoji;
auto &order = _controller->session().data().stickers().setsOrderRef();
int insertAtIndex = 0, currentIndex = order.indexOf(_setId);
auto &order = isMasks
? stickers.maskSetsOrderRef()
: stickers.setsOrderRef();
const auto insertAtIndex = 0, currentIndex = order.indexOf(_setId);
if (currentIndex != insertAtIndex) {
if (currentIndex > 0) {
order.removeAt(currentIndex);
@@ -515,8 +551,10 @@ void StickerSetBox::Inner::installDone(
if (customIt != sets.cend()) {
const auto custom = customIt->second.get();
for (const auto sticker : std::as_const(_pack)) {
int removeIndex = custom->stickers.indexOf(sticker);
if (removeIndex >= 0) custom->stickers.removeAt(removeIndex);
const int removeIndex = custom->stickers.indexOf(sticker);
if (removeIndex >= 0) {
custom->stickers.removeAt(removeIndex);
}
}
if (custom->stickers.isEmpty()) {
sets.erase(customIt);
@@ -524,14 +562,23 @@ void StickerSetBox::Inner::installDone(
}
if (result.type() == mtpc_messages_stickerSetInstallResultArchive) {
_controller->session().data().stickers().applyArchivedResult(
stickers.applyArchivedResult(
result.c_messages_stickerSetInstallResultArchive());
} else {
auto &storage = _controller->session().local();
if (wasArchived) {
_controller->session().local().writeArchivedStickers();
if (isMasks) {
storage.writeArchivedMasks();
} else {
storage.writeArchivedStickers();
}
}
_controller->session().local().writeInstalledStickers();
_controller->session().data().stickers().notifyUpdated();
if (isMasks) {
storage.writeInstalledMasks();
} else {
storage.writeInstalledStickers();
}
stickers.notifyUpdated();
}
_setInstalled.fire_copy(_setId);
}
@@ -652,6 +699,8 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
+ ((_elements.size() % kStickersPanelPerRow) ? 1 : 0);
int32 from = qFloor(e->rect().top() / st::stickersSize.height()), to = qFloor(e->rect().bottom() / st::stickersSize.height()) + 1;
_pathGradient->startFrame(0, width(), width() / 2);
for (int32 i = from; i < to; ++i) {
for (int32 j = 0; j < kStickersPanelPerRow; ++j) {
int32 index = i * kStickersPanelPerRow + j;
@@ -747,7 +796,8 @@ void StickerSetBox::Inner::paintSticker(
const auto &media = element.documentMedia;
media->checkStickerSmall();
if (document->sticker()->animated
const auto isAnimated = document->sticker()->animated;
if (isAnimated
&& !element.animated
&& media->loaded()) {
const_cast<Inner*>(this)->setupLottie(index);
@@ -755,7 +805,7 @@ void StickerSetBox::Inner::paintSticker(
auto w = 1;
auto h = 1;
if (element.animated && !document->dimensions.isEmpty()) {
if (isAnimated && !document->dimensions.isEmpty()) {
const auto request = Lottie::FrameRequest{ boundingBoxSize() * cIntRetinaFactor() };
const auto size = request.size(document->dimensions, true) / cIntRetinaFactor();
w = std::max(size.width(), 1);
@@ -767,6 +817,7 @@ void StickerSetBox::Inner::paintSticker(
h = std::max(qRound(coef * document->dimensions.height()), 1);
}
QPoint ppos = position + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2);
if (element.animated && element.animated->ready()) {
const auto frame = element.animated->frame();
p.drawImage(
@@ -779,6 +830,12 @@ void StickerSetBox::Inner::paintSticker(
ppos,
width(),
image->pix(w, h));
} else {
ChatHelpers::PaintStickerThumbnailPath(
p,
media.get(),
QRect(ppos, QSize(w, h)),
_pathGradient.get());
}
}
@@ -820,12 +877,7 @@ QString StickerSetBox::Inner::shortName() const {
}
void StickerSetBox::Inner::install() {
if (isMasksSet()) {
_controller->show(
Box<InformBox>(tr::lng_stickers_masks_pack(tr::now)),
Ui::LayerOption::KeepOther);
return;
} else if (_installRequest) {
if (_installRequest) {
return;
}
_installRequest = _api.request(MTPmessages_InstallStickerSet(
@@ -838,4 +890,17 @@ void StickerSetBox::Inner::install() {
}).send();
}
void StickerSetBox::Inner::archiveStickers() {
_api.request(MTPmessages_InstallStickerSet(
_input,
MTP_boolTrue()
)).done([=](const MTPmessages_StickerSetInstallResult &result) {
if (result.type() == mtpc_messages_stickerSetInstallResultSuccess) {
_setArchived.fire_copy(_setId);
}
}).fail([](const MTP::Error &error) {
Ui::Toast::Show(Lang::Hard::ServerError());
}).send();
}
StickerSetBox::Inner::~Inner() = default;

View File

@@ -46,7 +46,6 @@ private:
void updateButtons();
void addStickers();
void copyStickersLink();
void archiveStickers();
void handleError(Error error);
const not_null<Window::SessionController*> _controller;

View File

@@ -90,7 +90,7 @@ public:
void saveGroupSet();
void rebuild();
void rebuild(bool masks);
void updateSize(int newWidth = 0);
void updateRows(); // refresh only pack cover stickers
bool appendSet(not_null<StickersSet*> set);
@@ -152,6 +152,7 @@ private:
~Row();
bool isRecentSet() const;
bool isMasksSet() const;
const not_null<StickersSet*> set;
DocumentData *sticker = nullptr;
@@ -239,7 +240,8 @@ private:
const not_null<Window::SessionController*> _controller;
MTP::Sender _api;
Section _section;
const Section _section;
const bool _isInstalled;
int32 _rowHeight;
@@ -351,7 +353,7 @@ void StickersBox::Tab::returnWidget(object_ptr<Inner> widget) {
Assert(_widget == _weak);
}
StickersBox::Inner *StickersBox::Tab::widget() {
StickersBox::Inner *StickersBox::Tab::widget() const {
return _weak;
}
@@ -366,7 +368,8 @@ void StickersBox::Tab::saveScrollTop() {
StickersBox::StickersBox(
QWidget*,
not_null<Window::SessionController*> controller,
Section section)
Section section,
bool masks)
: _controller(controller)
, _api(&controller->session().mtp())
, _tabs(this, st::stickersTabs)
@@ -374,9 +377,11 @@ StickersBox::StickersBox(
this,
controller->session().data().stickers().featuredSetsUnreadCountValue())
, _section(section)
, _installed(0, this, controller, Section::Installed)
, _featured(1, this, controller, Section::Featured)
, _archived(2, this, controller, Section::Archived) {
, _isMasks(masks)
, _installed(_isMasks ? Tab() : Tab(0, this, controller, Section::Installed))
, _masks(_isMasks ? Tab(0, this, controller, Section::Masks) : Tab())
, _featured(_isMasks ? Tab() : Tab(1, this, controller, Section::Featured))
, _archived((_isMasks ? 1 : 2), this, controller, Section::Archived) {
_tabs->setRippleTopRoundRadius(st::boxRadius);
}
@@ -387,6 +392,7 @@ StickersBox::StickersBox(
: _controller(controller)
, _api(&controller->session().mtp())
, _section(Section::Installed)
, _isMasks(false)
, _installed(0, this, controller, megagroup)
, _megagroupSet(megagroup) {
_installed.widget()->scrollsToY(
@@ -402,6 +408,7 @@ StickersBox::StickersBox(
: _controller(controller)
, _api(&controller->session().mtp())
, _section(Section::Attached)
, _isMasks(false)
, _attached(0, this, controller, Section::Attached)
, _attachedSets(attachedSets) {
}
@@ -447,7 +454,7 @@ void StickersBox::getArchivedDone(
}
auto &stickers = result.c_messages_archivedStickers();
auto &archived = session().data().stickers().archivedSetsOrderRef();
auto &archived = archivedSetsOrderRef();
if (offsetId) {
auto index = archived.indexOf(offsetId);
if (index >= 0) {
@@ -514,7 +521,11 @@ void StickersBox::getArchivedDone(
void StickersBox::prepare() {
if (_section == Section::Installed) {
if (_tabs) {
session().local().readArchivedStickers();
if (_isMasks) {
session().local().readArchivedMasks();
} else {
session().local().readArchivedStickers();
}
} else {
setTitle(tr::lng_stickers_group_set());
}
@@ -524,7 +535,7 @@ void StickersBox::prepare() {
setTitle(tr::lng_stickers_attached_sets());
}
if (_tabs) {
if (session().data().stickers().archivedSetsOrder().isEmpty()) {
if (archivedSetsOrder().isEmpty()) {
preloadArchivedSets();
}
setNoContentMargin(true);
@@ -534,21 +545,33 @@ void StickersBox::prepare() {
lifetime());
refreshTabs();
}
if (_installed.widget() && _section != Section::Installed) _installed.widget()->hide();
if (_featured.widget() && _section != Section::Featured) _featured.widget()->hide();
if (_archived.widget() && _section != Section::Archived) _archived.widget()->hide();
if (_attached.widget() && _section != Section::Attached) _attached.widget()->hide();
if (_installed.widget() && _section != Section::Installed) {
_installed.widget()->hide();
}
if (_masks.widget() && _section != Section::Masks) {
_masks.widget()->hide();
}
if (_featured.widget() && _section != Section::Featured) {
_featured.widget()->hide();
}
if (_archived.widget() && _section != Section::Archived) {
_archived.widget()->hide();
}
if (_attached.widget() && _section != Section::Attached) {
_attached.widget()->hide();
}
const auto installCallback = [=](uint64 setId) { installSet(setId); };
if (_featured.widget()) {
_featured.widget()->setInstallSetCallback([this](uint64 setId) { installSet(setId); });
_featured.widget()->setInstallSetCallback(installCallback);
}
if (_archived.widget()) {
_archived.widget()->setInstallSetCallback([this](uint64 setId) { installSet(setId); });
_archived.widget()->setLoadMoreCallback([this] { loadMoreArchived(); });
_archived.widget()->setInstallSetCallback(installCallback);
_archived.widget()->setLoadMoreCallback([=] { loadMoreArchived(); });
}
if (_attached.widget()) {
_attached.widget()->setInstallSetCallback([this](uint64 setId) { installSet(setId); });
_attached.widget()->setLoadMoreCallback([this] { showAttachedStickers(); });
_attached.widget()->setInstallSetCallback(installCallback);
_attached.widget()->setLoadMoreCallback([=] { showAttachedStickers(); });
}
if (_megagroupSet) {
@@ -565,6 +588,8 @@ void StickersBox::prepare() {
if (_section == Section::Installed) {
_tab = &_installed;
} else if (_section == Section::Masks) {
_tab = &_masks;
} else if (_section == Section::Archived) {
_tab = &_archived;
} else if (_section == Section::Attached) {
@@ -580,18 +605,21 @@ void StickersBox::prepare() {
[this] { handleStickersUpdated(); },
lifetime());
session().api().updateStickers();
session().api().updateMasks();
if (_installed.widget()) {
_installed.widget()->draggingScrollDelta(
) | rpl::start_with_next([=](int delta) {
scrollByDraggingDelta(delta);
}, _installed.widget()->lifetime());
if (!_megagroupSet) {
boxClosing() | rpl::start_with_next([=] {
saveChanges();
}, lifetime());
for (const auto &widget : { _installed.widget(), _masks.widget() }) {
if (widget) {
widget->draggingScrollDelta(
) | rpl::start_with_next([=](int delta) {
scrollByDraggingDelta(delta);
}, widget->lifetime());
}
}
if (!_megagroupSet) {
boxClosing() | rpl::start_with_next([=] {
saveChanges();
}, lifetime());
}
if (_tabs) {
_tabs->raise();
@@ -601,28 +629,41 @@ void StickersBox::prepare() {
}
void StickersBox::refreshTabs() {
if (!_tabs) return;
if (!_tabs) {
return;
}
auto &stickers = session().data().stickers();
_tabIndices.clear();
auto sections = QStringList();
sections.push_back(tr::lng_stickers_installed_tab(tr::now).toUpper());
_tabIndices.push_back(Section::Installed);
if (!session().data().stickers().featuredSetsOrder().isEmpty()) {
auto sections = std::vector<QString>();
if (_installed.widget()) {
sections.push_back(tr::lng_stickers_installed_tab(tr::now).toUpper());
_tabIndices.push_back(Section::Installed);
}
if (_masks.widget()) {
sections.push_back(tr::lng_stickers_masks_tab(tr::now).toUpper());
_tabIndices.push_back(Section::Masks);
}
if (!stickers.featuredSetsOrder().isEmpty() && _featured.widget()) {
sections.push_back(tr::lng_stickers_featured_tab(tr::now).toUpper());
_tabIndices.push_back(Section::Featured);
}
if (!session().data().stickers().archivedSetsOrder().isEmpty()) {
if (!archivedSetsOrder().isEmpty() && _archived.widget()) {
sections.push_back(tr::lng_stickers_archived_tab(tr::now).toUpper());
_tabIndices.push_back(Section::Archived);
}
_tabs->setSections(sections);
if ((_tab == &_archived && !_tabIndices.contains(Section::Archived))
|| (_tab == &_featured && !_tabIndices.contains(Section::Featured))) {
|| (_tab == &_featured && !_tabIndices.contains(Section::Featured))
|| (_tab == &_masks && !_tabIndices.contains(Section::Masks))) {
switchTab();
} else if (_tab == &_archived) {
_tabs->setActiveSectionFast(_tabIndices.indexOf(Section::Archived));
} else if (_tab == &_featured) {
_tabs->setActiveSectionFast(_tabIndices.indexOf(Section::Featured));
} else if (_tab == &_masks) {
_tabs->setActiveSectionFast(_tabIndices.indexOf(Section::Masks));
}
updateTabsGeometry();
}
@@ -635,7 +676,7 @@ void StickersBox::loadMoreArchived() {
}
uint64 lastId = 0;
const auto &order = session().data().stickers().archivedSetsOrder();
const auto &order = archivedSetsOrder();
const auto &sets = session().data().stickers().sets();
for (auto setIt = order.cend(), e = order.cbegin(); setIt != e;) {
--setIt;
@@ -647,8 +688,11 @@ void StickersBox::loadMoreArchived() {
}
}
}
const auto flags = _isMasks
? MTPmessages_GetArchivedStickers::Flag::f_masks
: MTPmessages_GetArchivedStickers::Flags(0);
_archivedRequestId = _api.request(MTPmessages_GetArchivedStickers(
MTP_flags(0),
MTP_flags(flags),
MTP_long(lastId),
MTP_int(kArchivedLimitPerPage)
)).done([=](const MTPmessages_ArchivedStickers &result) {
@@ -674,13 +718,15 @@ void StickersBox::paintEvent(QPaintEvent *e) {
void StickersBox::updateTabsGeometry() {
if (!_tabs) return;
_tabs->resizeToWidth(_tabIndices.size() * width() / 3);
const auto maxTabs = _isMasks ? 2 : 3;
_tabs->resizeToWidth(_tabIndices.size() * width() / maxTabs);
_unreadBadge->setVisible(_tabIndices.contains(Section::Featured));
setInnerTopSkip(getTopSkip());
auto featuredLeft = width() / 3;
auto featuredRight = 2 * width() / 3;
auto featuredLeft = width() / maxTabs;
auto featuredRight = 2 * width() / maxTabs;
auto featuredTextWidth = st::stickersTabs.labelStyle.font->width(tr::lng_stickers_featured_tab(tr::now).toUpper());
auto featuredTextRight = featuredLeft + (featuredRight - featuredLeft - featuredTextWidth) / 2 + featuredTextWidth;
auto unreadBadgeLeft = featuredTextRight - st::stickersFeaturedBadgeSkip;
@@ -712,6 +758,9 @@ void StickersBox::switchTab() {
} else if (newSection == Section::Archived) {
newTab = &_archived;
requestArchivedSets();
} else if (newSection == Section::Masks) {
newTab = &_masks;
session().api().updateMasks();
}
if (_tab == newTab) {
onScrollToY(0);
@@ -744,7 +793,7 @@ void StickersBox::switchTab() {
_slideAnimation = std::make_unique<Ui::SlideAnimation>();
_slideAnimation->setSnapshots(std::move(wasCache), std::move(nowCache));
auto slideLeft = wasIndex > nowIndex;
_slideAnimation->start(slideLeft, [this] { update(); }, st::slideDuration);
_slideAnimation->start(slideLeft, [=] { update(); }, st::slideDuration);
setInnerVisible(false);
setFocus();
@@ -758,6 +807,16 @@ QPixmap StickersBox::grabContentCache() {
return result;
}
std::array<StickersBox::Inner*, 5> StickersBox::widgets() const {
return {
_installed.widget(),
_featured.widget(),
_archived.widget(),
_attached.widget(),
_masks.widget()
};
}
void StickersBox::installSet(uint64 setId) {
const auto &sets = session().data().stickers().sets();
const auto it = sets.find(setId);
@@ -769,10 +828,11 @@ void StickersBox::installSet(uint64 setId) {
const auto set = it->second.get();
if (_localRemoved.contains(setId)) {
_localRemoved.removeOne(setId);
if (_installed.widget()) _installed.widget()->setRemovedSets(_localRemoved);
if (_featured.widget()) _featured.widget()->setRemovedSets(_localRemoved);
if (_archived.widget()) _archived.widget()->setRemovedSets(_localRemoved);
if (_attached.widget()) _attached.widget()->setRemovedSets(_localRemoved);
for (const auto &widget : widgets()) {
if (widget) {
widget->setRemovedSets(_localRemoved);
}
}
}
if (!(set->flags & MTPDstickerSet::Flag::f_installed_date)
|| (set->flags & MTPDstickerSet::Flag::f_archived)) {
@@ -811,8 +871,11 @@ void StickersBox::preloadArchivedSets() {
return;
}
if (!_archivedRequestId) {
const auto flags = _isMasks
? MTPmessages_GetArchivedStickers::Flag::f_masks
: MTPmessages_GetArchivedStickers::Flags(0);
_archivedRequestId = _api.request(MTPmessages_GetArchivedStickers(
MTP_flags(0),
MTP_flags(flags),
MTP_long(0),
MTP_int(kArchivedLimitFirstRequest)
)).done([=](const MTPmessages_ArchivedStickers &result) {
@@ -828,7 +891,7 @@ void StickersBox::requestArchivedSets() {
}
const auto &sets = session().data().stickers().sets();
const auto &order = session().data().stickers().archivedSetsOrder();
const auto &order = archivedSetsOrder();
for (const auto setId : order) {
auto it = sets.find(setId);
if (it != sets.cend()) {
@@ -850,19 +913,22 @@ void StickersBox::resizeEvent(QResizeEvent *e) {
if (_titleShadow) {
_titleShadow->setGeometry(0, 0, width(), st::lineWidth);
}
if (_installed.widget()) _installed.widget()->resize(width(), _installed.widget()->height());
if (_featured.widget()) _featured.widget()->resize(width(), _featured.widget()->height());
if (_archived.widget()) _archived.widget()->resize(width(), _archived.widget()->height());
if (_attached.widget()) _attached.widget()->resize(width(), _attached.widget()->height());
for (const auto &widget : widgets()) {
if (widget) {
widget->resize(width(), widget->height());
}
}
}
void StickersBox::handleStickersUpdated() {
if (_section == Section::Installed || _section == Section::Featured) {
if (_section == Section::Installed
|| _section == Section::Featured
|| _section == Section::Masks) {
rebuildList();
} else {
_tab->widget()->updateRows();
}
if (session().data().stickers().archivedSetsOrder().isEmpty()) {
if (archivedSetsOrder().isEmpty()) {
preloadArchivedSets();
} else {
refreshTabs();
@@ -870,28 +936,55 @@ void StickersBox::handleStickersUpdated() {
}
void StickersBox::rebuildList(Tab *tab) {
if (_section == Section::Attached) return;
if (!tab) tab = _tab;
if (_section == Section::Attached) {
return;
}
if (!tab) {
tab = _tab;
}
if (tab == &_installed) {
if ((tab == &_installed) || (tab == &_masks)) {
_localOrder = tab->widget()->getFullOrder();
_localRemoved = tab->widget()->getRemovedSets();
}
tab->widget()->rebuild();
if (tab == &_installed) {
tab->widget()->rebuild(_isMasks);
if ((tab == &_installed) || (tab == &_masks)) {
tab->widget()->setFullOrder(_localOrder);
}
tab->widget()->setRemovedSets(_localRemoved);
}
void StickersBox::saveChanges() {
const auto installed = _installed.widget();
const auto masks = _masks.widget();
// Make sure that our changes in other tabs are applied in the Installed tab.
rebuildList(&_installed);
if (installed) {
rebuildList(&_installed);
}
if (masks) {
rebuildList(&_masks);
}
if (_someArchivedLoaded) {
session().local().writeArchivedStickers();
if (_isMasks) {
session().local().writeArchivedMasks();
} else {
session().local().writeArchivedStickers();
}
}
if (installed) {
session().api().saveStickerSets(
installed->getOrder(),
installed->getRemovedSets(),
false);
}
if (masks) {
session().api().saveStickerSets(
masks->getOrder(),
masks->getRemovedSets(),
true);
}
session().api().saveStickerSets(_installed.widget()->getOrder(), _installed.widget()->getRemovedSets());
}
void StickersBox::setInnerFocus() {
@@ -900,6 +993,18 @@ void StickersBox::setInnerFocus() {
}
}
const Data::StickersSetsOrder &StickersBox::archivedSetsOrder() const {
return !_isMasks
? session().data().stickers().archivedSetsOrder()
: session().data().stickers().archivedMaskSetsOrder();
}
Data::StickersSetsOrder &StickersBox::archivedSetsOrderRef() {
return !_isMasks
? session().data().stickers().archivedSetsOrderRef()
: session().data().stickers().archivedMaskSetsOrderRef();
}
StickersBox::~StickersBox() = default;
StickersBox::Inner::Row::Row(
@@ -932,7 +1037,12 @@ StickersBox::Inner::Row::Row(
StickersBox::Inner::Row::~Row() = default;
bool StickersBox::Inner::Row::isRecentSet() const {
return (set->id == Data::Stickers::CloudRecentSetId);
return (set->id == Data::Stickers::CloudRecentSetId)
|| (set->id == Data::Stickers::CloudRecentAttachedSetId);
}
bool StickersBox::Inner::Row::isMasksSet() const {
return (set->flags & MTPDstickerSet::Flag::f_masks);
}
StickersBox::Inner::Inner(
@@ -943,6 +1053,7 @@ StickersBox::Inner::Inner(
, _controller(controller)
, _api(&_controller->session().mtp())
, _section(section)
, _isInstalled(_section == Section::Installed || _section == Section::Masks)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _shiftingAnimation([=](crl::time now) {
return shiftingAnimationCallback(now);
@@ -963,6 +1074,7 @@ StickersBox::Inner::Inner(
, _controller(controller)
, _api(&_controller->session().mtp())
, _section(StickersBox::Section::Installed)
, _isInstalled(_section == Section::Installed || _section == Section::Masks)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _shiftingAnimation([=](crl::time now) {
return shiftingAnimationCallback(now);
@@ -1087,8 +1199,10 @@ QRect StickersBox::Inner::relativeButtonRect(bool removeButton) const {
auto buttonh = st::stickersRemove.height;
auto buttonshift = st::stickersRemoveSkip;
if (!removeButton) {
auto &st = (_section == Section::Installed) ? st::stickersUndoRemove : st::stickersTrendingAdd;
auto textWidth = (_section == Section::Installed) ? _undoWidth : _addWidth;
const auto &st = _isInstalled
? st::stickersUndoRemove
: st::stickersTrendingAdd;
const auto textWidth = _isInstalled ? _undoWidth : _addWidth;
buttonw = textWidth - st.width;
buttonh = st.height;
buttonshift = 0;
@@ -1117,7 +1231,7 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> row, int index) {
}
}
if (_section == Section::Installed) {
if (_isInstalled) {
if (index >= 0 && index == _above) {
auto current = _aboveShadowFadeOpacity.current();
if (_started >= 0) {
@@ -1144,13 +1258,13 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> row, int index) {
paintFakeButton(p, row, index);
}
if (row->removed && _section == Section::Installed) {
if (row->removed && _isInstalled) {
p.setOpacity(st::stickersRowDisabledOpacity);
}
auto stickerx = st::contactsPadding.left();
if (!_megagroupSet && _section == Section::Installed) {
if (!_megagroupSet && _isInstalled) {
stickerx += st::stickersReorderIcon.width() + st::stickersReorderSkip;
if (!row->isRecentSet()) {
st::stickersReorderIcon.paint(p, st::contactsPadding.left(), (_rowHeight - st::stickersReorderIcon.height()) / 2, width());
@@ -1181,7 +1295,11 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> row, int index) {
}
}
auto statusText = (row->count > 0) ? tr::lng_stickers_count(tr::now, lt_count, row->count) : tr::lng_contacts_loading(tr::now);
const auto statusText = (row->count == 0)
? tr::lng_contacts_loading(tr::now)
: row->isMasksSet()
? tr::lng_masks_count(tr::now, lt_count, row->count)
: tr::lng_stickers_count(tr::now, lt_count, row->count);
p.setFont(st::contactsStatusFont);
p.setPen(st::contactsStatusFg);
@@ -1281,7 +1399,7 @@ void StickersBox::Inner::updateRowThumbnail(not_null<Row*> row) {
Unexpected("StickersBox::Inner::updateRowThumbnail: row not found");
}();
const auto left = st::contactsPadding.left()
+ ((!_megagroupSet && _section == Section::Installed)
+ ((!_megagroupSet && _isInstalled)
? st::stickersReorderIcon.width() + st::stickersReorderSkip
: 0);
update(
@@ -1292,9 +1410,9 @@ void StickersBox::Inner::updateRowThumbnail(not_null<Row*> row) {
}
void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int index) {
auto removeButton = (_section == Section::Installed && !row->removed);
auto removeButton = (_isInstalled && !row->removed);
auto rect = relativeButtonRect(removeButton);
if (_section != Section::Installed && row->installed && !row->archived && !row->removed) {
if (!_isInstalled && row->installed && !row->archived && !row->removed) {
// Checkbox after installed from Trending or Archived.
int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (rect.width() + st::stickersFeaturedInstalled.width()) / 2);
int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2;
@@ -1317,10 +1435,12 @@ void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int ind
} else {
// Round button ADD when not installed from Trending or Archived.
// Or round button UNDO after disabled from Installed.
auto &st = (_section == Section::Installed) ? st::stickersUndoRemove : st::stickersTrendingAdd;
auto textWidth = (_section == Section::Installed) ? _undoWidth : _addWidth;
auto &text = (_section == Section::Installed) ? _undoText : _addText;
auto &textBg = selected ? st.textBgOver : st.textBg;
const auto &st = _isInstalled
? st::stickersUndoRemove
: st::stickersTrendingAdd;
const auto textWidth = _isInstalled ? _undoWidth : _addWidth;
const auto &text = _isInstalled ? _undoText : _addText;
const auto &textBg = selected ? st.textBgOver : st.textBg;
Ui::FillRoundRect(p, myrtlrect(rect), textBg, ImageRoundRadius::Small);
if (row->ripple) {
row->ripple->paint(p, rect.x(), rect.y(), width());
@@ -1345,7 +1465,7 @@ void StickersBox::Inner::mousePressEvent(QMouseEvent *e) {
setActionDown(_actionSel);
update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight);
} else if (auto selectedIndex = std::get_if<int>(&_selected)) {
if (_section == Section::Installed && !_rows[*selectedIndex]->isRecentSet() && _inDragArea) {
if (_isInstalled && !_rows[*selectedIndex]->isRecentSet() && _inDragArea) {
_above = _dragging = _started = *selectedIndex;
_dragStart = mapFromGlobal(_mouse);
}
@@ -1367,9 +1487,9 @@ void StickersBox::Inner::setActionDown(int newActionDown) {
if (_actionDown >= 0 && _actionDown < _rows.size()) {
update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight);
const auto row = _rows[_actionDown].get();
auto removeButton = (_section == Section::Installed && !row->removed);
auto removeButton = (_isInstalled && !row->removed);
if (!row->ripple) {
if (_section == Section::Installed) {
if (_isInstalled) {
if (row->removed) {
auto rippleSize = QSize(_undoWidth - st::stickersUndoRemove.width, st::stickersUndoRemove.height);
auto rippleMask = Ui::RippleAnimation::roundRectMask(rippleSize, st::roundRadiusSmall);
@@ -1512,14 +1632,14 @@ void StickersBox::Inner::updateSelected() {
selected = selectedIndex;
local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight);
const auto row = _rows[selectedIndex].get();
if (!_megagroupSet && (_section == Section::Installed || !row->installed || row->archived || row->removed)) {
auto removeButton = (_section == Section::Installed && !row->removed);
if (!_megagroupSet && (_isInstalled || !row->installed || row->archived || row->removed)) {
auto removeButton = (_isInstalled && !row->removed);
auto rect = myrtlrect(relativeButtonRect(removeButton));
actionSel = rect.contains(local) ? selectedIndex : -1;
} else {
actionSel = -1;
}
if (!_megagroupSet && _section == Section::Installed && !row->isRecentSet()) {
if (!_megagroupSet && _isInstalled && !row->isRecentSet()) {
auto dragAreaWidth = st::contactsPadding.left() + st::stickersReorderIcon.width() + st::stickersReorderSkip;
auto dragArea = myrtlrect(0, 0, dragAreaWidth, _rowHeight);
inDragArea = dragArea.contains(local);
@@ -1543,7 +1663,7 @@ void StickersBox::Inner::updateSelected() {
void StickersBox::Inner::updateCursor() {
setCursor(_inDragArea
? style::cur_sizeall
: (!_megagroupSet && _section == Section::Installed)
: (!_megagroupSet && _isInstalled)
? ((_actionSel >= 0 && (_actionDown < 0 || _actionDown == _actionSel))
? style::cur_pointer
: style::cur_default)
@@ -1568,7 +1688,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
_mouse = e->globalPos();
updateSelected();
if (_actionDown == _actionSel && _actionSel >= 0) {
if (_section == Section::Installed) {
if (_isInstalled) {
setRowRemoved(_actionDown, !_rows[_actionDown]->removed);
} else if (_installSetCallback) {
_installSetCallback(_rows[_actionDown]->set->id);
@@ -1623,7 +1743,8 @@ void StickersBox::Inner::saveGroupSet() {
: 0;
if (newId != oldId) {
session().api().setGroupStickerSet(_megagroupSet, _megagroupSetInput);
session().api().stickerSetInstalled(Data::Stickers::MegagroupSetId);
session().data().stickers().notifyStickerSetInstalled(
Data::Stickers::MegagroupSetId);
}
}
@@ -1840,7 +1961,7 @@ void StickersBox::Inner::rebuildMegagroupSet() {
}
}
void StickersBox::Inner::rebuild() {
void StickersBox::Inner::rebuild(bool masks) {
_itemsTop = st::membersMarginTop;
if (_megagroupSet) {
@@ -1859,10 +1980,14 @@ void StickersBox::Inner::rebuild() {
return session().data().stickers().featuredSetsOrder();
}
return result;
} else if (_section == Section::Masks) {
return session().data().stickers().maskSetsOrder();
} else if (_section == Section::Featured) {
return session().data().stickers().featuredSetsOrder();
}
return session().data().stickers().archivedSetsOrder();
return masks
? session().data().stickers().archivedMaskSetsOrder()
: session().data().stickers().archivedSetsOrder();
})();
_rows.reserve(order.size() + 1);
_shiftingStartTimes.reserve(order.size() + 1);
@@ -1874,8 +1999,10 @@ void StickersBox::Inner::rebuild() {
? tr::lng_stickers_group_from_featured(tr::now)
: tr::lng_stickers_group_from_your(tr::now));
updateControlsGeometry();
} else if (_section == Section::Installed) {
auto cloudIt = sets.find(Data::Stickers::CloudRecentSetId);
} else if (_isInstalled) {
const auto cloudIt = sets.find((_section == Section::Masks)
? Data::Stickers::CloudRecentAttachedSetId
: Data::Stickers::CloudRecentSetId); // Section::Installed.
if (cloudIt != sets.cend() && !cloudIt->second->stickers.isEmpty()) {
rebuildAppendSet(cloudIt->second.get(), maxNameWidth);
}
@@ -1900,7 +2027,7 @@ void StickersBox::Inner::rebuild() {
void StickersBox::Inner::setMegagroupSelectedSet(const MTPInputStickerSet &set) {
_megagroupSetInput = set;
rebuild();
rebuild(false);
_scrollsToY.fire(0);
updateSelected();
}
@@ -1944,7 +2071,7 @@ void StickersBox::Inner::updateRows() {
auto wasInstalled = row->installed;
auto wasArchived = row->archived;
fillSetFlags(set, &row->installed, &row->official, &row->unread, &row->archived);
if (_section == Section::Installed) {
if (_isInstalled) {
row->archived = false;
}
if (row->installed != wasInstalled || row->archived != wasArchived) {
@@ -1969,11 +2096,11 @@ bool StickersBox::Inner::appendSet(not_null<StickersSet*> set) {
int StickersBox::Inner::countMaxNameWidth() const {
int namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left();
if (!_megagroupSet && _section == Section::Installed) {
if (!_megagroupSet && _isInstalled) {
namex += st::stickersReorderIcon.width() + st::stickersReorderSkip;
}
int namew = st::boxWideWidth - namex - st::contactsPadding.right() - st::contactsCheckPosition.x();
if (_section == Section::Installed) {
if (_isInstalled) {
if (!_megagroupSet) {
namew -= _undoWidth - st::stickersUndoRemove.width;
}
@@ -1993,7 +2120,7 @@ void StickersBox::Inner::rebuildAppendSet(
if (set->id != Data::Stickers::CloudRecentSetId) {
fillSetFlags(set, &installed, &official, &unread, &archived);
}
if (_section == Section::Installed && archived) {
if (_isInstalled && archived) {
return;
}

View File

@@ -56,12 +56,14 @@ public:
Featured,
Archived,
Attached,
Masks,
};
StickersBox(
QWidget*,
not_null<Window::SessionController*> controller,
Section section);
Section section,
bool masks = false);
StickersBox(
QWidget*,
not_null<Window::SessionController*> controller,
@@ -94,7 +96,7 @@ private:
object_ptr<Inner> takeWidget();
void returnWidget(object_ptr<Inner> widget);
[[nodiscard]] Inner *widget();
[[nodiscard]] Inner *widget() const;
[[nodiscard]] int index() const;
void saveScrollTop();
@@ -103,7 +105,7 @@ private:
}
private:
int _index = 0;
const int _index = 0;
object_ptr<Inner> _widget = { nullptr };
QPointer<Inner> _weak;
int _scrollTop = 0;
@@ -132,6 +134,11 @@ private:
uint64 offsetId);
void showAttachedStickers();
const Data::StickersSetsOrder &archivedSetsOrder() const;
Data::StickersSetsOrder &archivedSetsOrderRef();
std::array<Inner*, 5> widgets() const;
const not_null<Window::SessionController*> _controller;
MTP::Sender _api;
@@ -142,8 +149,10 @@ private:
object_ptr<CounterWidget> _unreadBadge = { nullptr };
Section _section;
const bool _isMasks;
Tab _installed;
Tab _masks;
Tab _featured;
Tab _archived;
Tab _attached;

View File

@@ -223,6 +223,7 @@ void Panel::Incoming::RendererGL::paint(
Assert(data.format == Webrtc::FrameFormat::YUV420);
Assert(!data.yuv420->size.isEmpty());
const auto yuv = data.yuv420;
const auto format = Ui::GL::CurrentSingleComponentFormat();
f.glActiveTexture(GL_TEXTURE0);
_textures.bind(f, 1);
@@ -230,8 +231,8 @@ void Panel::Incoming::RendererGL::paint(
f.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
uploadTexture(
f,
GL_RED,
GL_RED,
format,
format,
yuv->size,
_lumaSize,
yuv->y.stride,
@@ -243,8 +244,8 @@ void Panel::Incoming::RendererGL::paint(
if (upload) {
uploadTexture(
f,
GL_RED,
GL_RED,
format,
format,
yuv->chromaSize,
_chromaSize,
yuv->u.stride,
@@ -255,8 +256,8 @@ void Panel::Incoming::RendererGL::paint(
if (upload) {
uploadTexture(
f,
GL_RED,
GL_RED,
format,
format,
yuv->chromaSize,
_chromaSize,
yuv->v.stride,

View File

@@ -48,11 +48,13 @@ void EditGroupCallTitleBox(
box->setFocusCallback([=] {
input->setFocusFast();
});
box->addButton(tr::lng_settings_save(), [=] {
const auto submit = [=] {
const auto result = input->getLastText().trimmed();
box->closeBox();
done(result);
});
};
QObject::connect(input, &Ui::InputField::submitted, submit);
box->addButton(tr::lng_settings_save(), submit);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
@@ -76,11 +78,13 @@ void StartGroupCallRecordingBox(
box->setFocusCallback([=] {
input->setFocusFast();
});
box->addButton(tr::lng_group_call_recording_start_button(), [=] {
const auto submit = [=] {
const auto result = input->getLastText().trimmed();
box->closeBox();
done(result);
});
};
QObject::connect(input, &Ui::InputField::submitted, submit);
box->addButton(tr::lng_group_call_recording_start_button(), submit);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}

View File

@@ -988,6 +988,7 @@ void Viewport::RendererGL::bindFrame(
program.argb32->setUniformValue("s_texture", GLint(0));
} else {
const auto yuv = data.yuv420;
const auto format = Ui::GL::CurrentSingleComponentFormat();
program.yuv420->bind();
f.glActiveTexture(GL_TEXTURE0);
tileData.textures.bind(f, 0);
@@ -995,8 +996,8 @@ void Viewport::RendererGL::bindFrame(
f.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
uploadTexture(
f,
GL_RED,
GL_RED,
format,
format,
yuv->size,
tileData.textureSize,
yuv->y.stride,
@@ -1009,8 +1010,8 @@ void Viewport::RendererGL::bindFrame(
if (upload) {
uploadTexture(
f,
GL_RED,
GL_RED,
format,
format,
yuv->chromaSize,
tileData.textureChromaSize,
yuv->u.stride,
@@ -1021,8 +1022,8 @@ void Viewport::RendererGL::bindFrame(
if (upload) {
uploadTexture(
f,
GL_RED,
GL_RED,
format,
format,
yuv->chromaSize,
tileData.textureChromaSize,
yuv->v.stride,
@@ -1368,16 +1369,17 @@ void Viewport::RendererGL::validateNoiseTexture(
if (_noiseTexture.created()) {
return;
}
const auto format = Ui::GL::CurrentSingleComponentFormat();
_noiseTexture.ensureCreated(f, GL_NEAREST, GL_REPEAT);
_noiseTexture.bind(f, 0);
f.glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
format,
kNoiseTextureSize,
kNoiseTextureSize,
0,
GL_RED,
format,
GL_UNSIGNED_BYTE,
nullptr);

View File

@@ -171,11 +171,11 @@ bool BotKeyboard::moderateKeyActivate(int key) {
App::sendBotCommand(user, user, qsl("/pattern"));
} else if (key == Qt::Key_4) {
App::sendBotCommand(user, user, qsl("/abuse"));
} else if (key == Qt::Key_0 || key == Qt::Key_E) {
} else if (key == Qt::Key_0) {
App::sendBotCommand(user, user, qsl("/undo"));
} else if (key == Qt::Key_Plus || key == Qt::Key_QuoteLeft) {
} else if (key == Qt::Key_7) {
App::sendBotCommand(user, user, qsl("/next"));
} else if (key == Qt::Key_Period || key == Qt::Key_S) {
} else if (key == Qt::Key_8) {
App::sendBotCommand(user, user, qsl("/stats"));
}
return true;
@@ -201,6 +201,7 @@ bool BotKeyboard::updateMarkup(HistoryItem *to, bool force) {
if (_wasForMsgId.msg) {
_maximizeSize = _singleUse = _forceReply = false;
_wasForMsgId = FullMsgId();
_placeholder = QString();
_impl = nullptr;
return true;
}
@@ -218,6 +219,12 @@ bool BotKeyboard::updateMarkup(HistoryItem *to, bool force) {
_maximizeSize = !(markupFlags & MTPDreplyKeyboardMarkup::Flag::f_resize);
_singleUse = _forceReply || (markupFlags & MTPDreplyKeyboardMarkup::Flag::f_single_use);
if (const auto markup = to->Get<HistoryMessageReplyMarkup>()) {
_placeholder = markup->placeholder;
} else {
_placeholder = QString();
}
_impl = nullptr;
if (auto markup = to->Get<HistoryMessageReplyMarkup>()) {
if (!markup->rows.empty()) {

View File

@@ -31,8 +31,12 @@ public:
// With force=true the markup is updated even if it is
// already shown for the passed history item.
bool updateMarkup(HistoryItem *last, bool force = false);
bool hasMarkup() const;
bool forceReply() const;
[[nodiscard]] bool hasMarkup() const;
[[nodiscard]] bool forceReply() const;
[[nodiscard]] QString placeholder() const {
return _placeholder;
}
void step_selected(crl::time ms, bool timer);
void resizeToWidth(int newWidth, int maxOuterHeight) {
@@ -40,10 +44,10 @@ public:
return TWidget::resizeToWidth(newWidth);
}
bool maximizeSize() const;
bool singleUse() const;
[[nodiscard]] bool maximizeSize() const;
[[nodiscard]] bool singleUse() const;
FullMsgId forMsgId() const {
[[nodiscard]] FullMsgId forMsgId() const {
return _wasForMsgId;
}
@@ -76,6 +80,7 @@ private:
const not_null<Main::Session*> _session;
FullMsgId _wasForMsgId;
QString _placeholder;
int _height = 0;
int _maxOuterHeight = 0;
bool _maximizeSize = false;

View File

@@ -15,10 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h"
#include "ui/effects/radial_animation.h"
#include "ui/emoji_config.h"
#include "ui/ui_utility.h"
#include "core/application.h"
#include "main/main_account.h"
#include "mainwidget.h"
#include "app.h"
#include "storage/storage_cloud_blob.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -456,7 +456,7 @@ void Row::setupPreview(const Set &set) {
const auto full = original.height();
auto &&preview = ranges::views::zip(_preview, ranges::views::ints(0, int(_preview.size())));
for (auto &&[pixmap, index] : preview) {
pixmap = App::pixmapFromImageInPlace(original.copy(
pixmap = Ui::PixmapFromImage(original.copy(
{ full * index, 0, full, full }
).scaledToWidth(size, Qt::SmoothTransformation));
pixmap.setDevicePixelRatio(cRetinaFactor());

View File

@@ -29,7 +29,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/input_fields.h"
#include "ui/text/text_options.h"
#include "ui/image/image.h"
#include "ui/effects/path_shift_gradient.h"
#include "ui/ui_utility.h"
#include "ui/cached_round_corners.h"
#include "base/unixtime.h"
@@ -125,6 +127,8 @@ private:
bool _isOneColumn = false;
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
Fn<SendMenu::Type()> _sendMenuType;
rpl::event_stream<FieldAutocomplete::MentionChosen> _mentionChosen;
@@ -438,23 +442,24 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
} else if (_type == Type::BotCommands) {
bool listAllSuggestions = _filter.isEmpty();
bool hasUsername = _filter.indexOf('@') > 0;
base::flat_map<UserData*, bool> bots;
base::flat_map<
not_null<UserData*>,
not_null<const std::vector<BotCommand>*>> bots;
int32 cnt = 0;
if (_chat) {
if (_chat->noParticipantInfo()) {
_chat->session().api().requestFullPeer(_chat);
} else if (!_chat->participants.empty()) {
const auto &commands = _chat->botCommands();
for (const auto user : _chat->participants) {
if (!user->isBot()) {
continue;
} else if (!user->botInfo->inited) {
user->session().api().requestFullPeer(user);
}
if (user->botInfo->commands.isEmpty()) {
continue;
const auto i = commands.find(peerToUser(user->id));
if (i != end(commands)) {
bots.emplace(user, &i->second);
cnt += i->second.size();
}
bots.emplace(user, true);
cnt += user->botInfo->commands.size();
}
}
} else if (_user && _user->isBot()) {
@@ -462,65 +467,71 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
_user->session().api().requestFullPeer(_user);
}
cnt = _user->botInfo->commands.size();
bots.emplace(_user, true);
bots.emplace(_user, &_user->botInfo->commands);
} else if (_channel && _channel->isMegagroup()) {
if (_channel->mgInfo->bots.empty()) {
if (!_channel->mgInfo->botStatus) {
_channel->session().api().requestBots(_channel);
}
} else {
const auto &commands = _channel->mgInfo->botCommands();
for (const auto user : _channel->mgInfo->bots) {
if (!user->isBot()) {
continue;
} else if (!user->botInfo->inited) {
user->session().api().requestFullPeer(user);
}
if (user->botInfo->commands.isEmpty()) {
continue;
const auto i = commands.find(peerToUser(user->id));
if (i != end(commands)) {
bots.emplace(user, &i->second);
cnt += i->second.size();
}
bots.emplace(user, true);
cnt += user->botInfo->commands.size();
}
}
}
if (cnt) {
const auto make = [&](
not_null<UserData*> user,
const BotCommand &command) {
return BotCommandRow{
user,
command.command,
command.description,
user->activeUserpicView()
};
};
brows.reserve(cnt);
int32 botStatus = _chat ? _chat->botStatus : ((_channel && _channel->isMegagroup()) ? _channel->mgInfo->botStatus : -1);
if (_chat) {
for (const auto &user : _chat->lastAuthors) {
if (!user->isBot()) {
continue;
} else if (!bots.contains(user)) {
continue;
} else if (!user->botInfo->inited) {
user->session().api().requestFullPeer(user);
}
if (user->botInfo->commands.isEmpty()) {
const auto i = bots.find(user);
if (i == end(bots)) {
continue;
}
bots.remove(user);
for (auto j = 0, l = user->botInfo->commands.size(); j != l; ++j) {
for (const auto &command : *i->second) {
if (!listAllSuggestions) {
auto toFilter = (hasUsername || botStatus == 0 || botStatus == 2)
? user->botInfo->commands.at(j).command + '@' + user->username
: user->botInfo->commands.at(j).command;
? command.command + '@' + user->username
: command.command;
if (!toFilter.startsWith(_filter, Qt::CaseInsensitive)/* || toFilter.size() == _filter.size()*/) {
continue;
}
}
brows.push_back({ user, &user->botInfo->commands.at(j) });
brows.push_back(make(user, command));
}
bots.erase(i);
}
}
if (!bots.empty()) {
for (auto i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
UserData *user = i->first;
for (int32 j = 0, l = user->botInfo->commands.size(); j < l; ++j) {
const auto user = i->first;
for (const auto &command : *i->second) {
if (!listAllSuggestions) {
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? command.command + '@' + user->username : command.command;
if (!toFilter.startsWith(_filter, Qt::CaseInsensitive)/* || toFilter.size() == _filter.size()*/) continue;
}
brows.push_back({ user, &user->botInfo->commands.at(j) });
brows.push_back(make(user, command));
}
}
}
@@ -738,6 +749,10 @@ FieldAutocomplete::Inner::Inner(
, _hrows(hrows)
, _brows(brows)
, _srows(srows)
, _pathGradient(std::make_unique<Ui::PathShiftGradient>(
st::windowBgRipple,
st::windowBgOver,
[=] { update(); }))
, _previewTimer([=] { showPreview(); }) {
controller->session().downloaderTaskFinished(
) | rpl::start_with_next([=] {
@@ -770,6 +785,11 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
- st::defaultScrollArea.width;
if (!_srows->empty()) {
_pathGradient->startFrame(
0,
width(),
std::min(st::msgMaxWidth / 2, width() / 2));
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);
@@ -815,6 +835,7 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
w = std::max(qRound(coef * document->dimensions.width()), 1);
h = std::max(qRound(coef * document->dimensions.height()), 1);
}
if (sticker.animated && sticker.animated->ready()) {
const auto frame = sticker.animated->frame();
const auto size = frame.size() / cIntRetinaFactor();
@@ -832,6 +853,13 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
} else if (const auto image = media->getStickerSmall()) {
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
p.drawPixmapLeft(ppos, width(), image->pix(w, h));
} else {
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
ChatHelpers::PaintStickerThumbnailPath(
p,
media.get(),
QRect(ppos, QSize(w, h)),
_pathGradient.get());
}
}
}
@@ -920,8 +948,7 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
auto &row = _brows->at(i);
const auto user = row.user;
const auto command = row.command;
auto toHighlight = command->command;
auto toHighlight = row.command;
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
if (hasUsername || botStatus == 0 || botStatus == 2) {
toHighlight += '@' + user->username;
@@ -939,9 +966,16 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
auto addleft = commandTextWidth + st::mentionPadding.left();
auto widthleft = mentionwidth - addleft;
if (widthleft > st::mentionFont->elidew && !command->descriptionText().isEmpty()) {
if (!row.description.isEmpty()
&& row.descriptionText.isEmpty()) {
row.descriptionText.setText(
st::defaultTextStyle,
row.description,
Ui::NameTextOptions());
}
if (widthleft > st::mentionFont->elidew && !row.descriptionText.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
command->descriptionText().drawElided(p, mentionleft + addleft, i * st::mentionHeight + st::mentionTop, widthleft);
row.descriptionText.drawElided(p, mentionleft + addleft, i * st::mentionHeight + st::mentionTop, widthleft);
}
}
}
@@ -1043,7 +1077,7 @@ bool FieldAutocomplete::Inner::chooseAtIndex(
} else if (!_brows->empty()) {
if (index < _brows->size()) {
const auto user = _brows->at(index).user;
const auto command = _brows->at(index).command;
const auto &command = _brows->at(index).command;
const auto botStatus = _parent->chat()
? _parent->chat()->botStatus
: ((_parent->channel() && _parent->channel()->isMegagroup())
@@ -1054,7 +1088,7 @@ bool FieldAutocomplete::Inner::chooseAtIndex(
|| botStatus == 2
|| _parent->filter().indexOf('@') > 0);
const auto commandString = QString("/%1%2").arg(
command->command,
command,
insertUsername ? ('@' + user->username) : QString());
_botCommandChosen.fire({ commandString, method });

View File

@@ -136,8 +136,10 @@ private:
struct BotCommandRow {
not_null<UserData*> user;
not_null<const BotCommand*> command;
QString command;
QString description;
std::shared_ptr<Data::CloudImageView> userpic;
Ui::Text::String descriptionText;
};
using HashtagRows = std::vector<QString>;

View File

@@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/popup_menu.h"
#include "ui/effects/animations.h"
#include "ui/effects/ripple_animation.h"
#include "ui/effects/path_shift_gradient.h"
#include "ui/image/image.h"
#include "ui/cached_round_corners.h"
#include "lottie/lottie_multi_player.h"
@@ -110,7 +111,9 @@ struct StickerIcon {
class StickersListWidget::Footer : public TabbedSelector::InnerFooter {
public:
explicit Footer(not_null<StickersListWidget*> parent);
explicit Footer(
not_null<StickersListWidget*> parent,
bool searchButtonVisible);
void preloadImages();
void validateSelectedIcon(
@@ -127,6 +130,8 @@ public:
void clearHeavyData();
rpl::producer<> openSettingsRequests() const;
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
@@ -145,8 +150,7 @@ private:
};
using OverState = std::variant<SpecialOver, int>;
template <typename Callback>
void enumerateVisibleIcons(Callback callback);
void enumerateVisibleIcons(Fn<void(const StickerIcon &, int)> callback);
bool iconsAnimationCallback(crl::time now);
void setSelectedIcon(
@@ -170,6 +174,7 @@ private:
void scrollByWheelEvent(not_null<QWheelEvent*> e);
const not_null<StickersListWidget*> _pan;
const bool _searchButtonVisible = true;
static constexpr auto kVisibleIconsCount = 8;
@@ -196,6 +201,8 @@ private:
object_ptr<Ui::CrossButton> _searchCancel = { nullptr };
QPointer<QWidget> _focusTakenFrom;
rpl::event_stream<> _openSettingsRequests;
};
auto StickersListWidget::PrepareStickers(
@@ -240,15 +247,21 @@ void StickersListWidget::Sticker::ensureMediaCreated() {
documentMedia = document->createMediaView();
}
StickersListWidget::Footer::Footer(not_null<StickersListWidget*> parent)
StickersListWidget::Footer::Footer(
not_null<StickersListWidget*> parent,
bool searchButtonVisible)
: InnerFooter(parent)
, _pan(parent)
, _searchButtonVisible(searchButtonVisible)
, _iconsAnimation([=](crl::time now) {
return iconsAnimationCallback(now);
}) {
setMouseTracking(true);
_iconsLeft = _iconsRight = st::emojiCategorySkip + st::stickerIconWidth;
_iconsLeft = st::emojiCategorySkip + (_searchButtonVisible
? st::stickerIconWidth
: 0);
_iconsRight = st::emojiCategorySkip + st::stickerIconWidth;
_pan->session().downloaderTaskFinished(
) | rpl::start_with_next([=] {
@@ -340,13 +353,17 @@ void StickersListWidget::Footer::returnFocus() {
}
}
template <typename Callback>
void StickersListWidget::Footer::enumerateVisibleIcons(Callback callback) {
void StickersListWidget::Footer::enumerateVisibleIcons(
Fn<void(const StickerIcon &, int)> callback) {
auto iconsX = qRound(_iconsX.current());
auto index = iconsX / st::stickerIconWidth;
auto x = _iconsLeft - (iconsX % st::stickerIconWidth);
auto first = floorclamp(iconsX, st::stickerIconWidth, 0, _icons.size());
auto last = ceilclamp(iconsX + width(), st::stickerIconWidth, 0, _icons.size());
auto last = ceilclamp(
iconsX + width(),
st::stickerIconWidth,
0,
_icons.size());
for (auto index = first; index != last; ++index) {
callback(_icons[index], x);
x += st::stickerIconWidth;
@@ -526,6 +543,10 @@ void StickersListWidget::Footer::resizeSearchControls() {
_searchCancel->moveToRight(st::gifsSearchCancelPosition.x(), st::gifsSearchCancelPosition.y());
}
rpl::producer<> StickersListWidget::Footer::openSettingsRequests() const {
return _openSettingsRequests.events();
}
void StickersListWidget::Footer::mousePressEvent(QMouseEvent *e) {
if (e->button() != Qt::LeftButton) {
return;
@@ -534,11 +555,7 @@ void StickersListWidget::Footer::mousePressEvent(QMouseEvent *e) {
updateSelected();
if (_iconOver == SpecialOver::Settings) {
_pan->controller()->show(Box<StickersBox>(
_pan->controller(),
(hasOnlyFeaturedSets()
? StickersBox::Section::Featured
: StickersBox::Section::Installed)));
_openSettingsRequests.fire({});
} else if (_iconOver == SpecialOver::Search) {
toggleSearch(true);
} else {
@@ -668,7 +685,8 @@ void StickersListWidget::Footer::updateSelected() {
const auto settingsLeft = width() - _iconsRight;
const auto searchLeft = _iconsLeft - st::stickerIconWidth;
auto newOver = OverState(SpecialOver::None);
if (x >= searchLeft
if (_searchButtonVisible
&& x >= searchLeft
&& x < searchLeft + st::stickerIconWidth
&& y >= _iconsTop
&& y < _iconsTop + st::emojiFooterHeight) {
@@ -752,12 +770,21 @@ bool StickersListWidget::Footer::hasOnlyFeaturedSets() const {
void StickersListWidget::Footer::paintStickerSettingsIcon(Painter &p) const {
const auto settingsLeft = width() - _iconsRight;
st::stickersSettings.paint(p, settingsLeft + (st::stickerIconWidth - st::stickersSettings.width()) / 2, _iconsTop + st::emojiCategory.iconPosition.y(), width());
st::stickersSettings.paint(
p,
settingsLeft
+ (st::stickerIconWidth - st::stickersSettings.width()) / 2,
_iconsTop + st::emojiCategory.iconPosition.y(),
width());
}
void StickersListWidget::Footer::paintSearchIcon(Painter &p) const {
const auto searchLeft = _iconsLeft - st::stickerIconWidth;
st::stickersSearch.paint(p, searchLeft + (st::stickerIconWidth - st::stickersSearch.width()) / 2, _iconsTop + st::emojiCategory.iconPosition.y(), width());
st::stickersSearch.paint(
p,
searchLeft + (st::stickerIconWidth - st::stickersSearch.width()) / 2,
_iconsTop + st::emojiCategory.iconPosition.y(),
width());
}
void StickersListWidget::Footer::validateIconLottieAnimation(
@@ -895,10 +922,16 @@ bool StickersListWidget::Footer::iconsAnimationCallback(crl::time now) {
StickersListWidget::StickersListWidget(
QWidget *parent,
not_null<Window::SessionController*> controller)
not_null<Window::SessionController*> controller,
bool masks)
: Inner(parent, controller)
, _api(&controller->session().mtp())
, _section(Section::Stickers)
, _isMasks(masks)
, _pathGradient(std::make_unique<Ui::PathShiftGradient>(
st::windowBgRipple,
st::windowBgOver,
[=] { update(); }))
, _megagroupSetAbout(st::columnMinimalWidthThird - st::emojiScroll.width - st::emojiPanHeaderLeft)
, _addText(tr::lng_stickers_featured_add(tr::now).toUpper())
, _addWidth(st::stickersTrendingAdd.font->width(_addText))
@@ -909,8 +942,10 @@ StickersListWidget::StickersListWidget(
setAttribute(Qt::WA_OpaquePaintEvent);
_settings->addClickHandler([=] {
using Section = StickersBox::Section;
controller->show(
Box<StickersBox>(controller, StickersBox::Section::Installed));
Box<StickersBox>(controller, Section::Installed, _isMasks),
Ui::LayerOption::KeepOther);
});
session().downloaderTaskFinished(
@@ -930,7 +965,11 @@ StickersListWidget::StickersListWidget(
}, lifetime());
session().data().stickers().recentUpdated(
) | rpl::start_with_next([=] {
) | rpl::start_with_next([=](Data::Stickers::Recent recent) {
const auto attached = (recent == Data::Stickers::Recent::Attached);
if (attached != _isMasks) {
return;
}
refreshRecent();
}, lifetime());
}
@@ -954,8 +993,22 @@ rpl::producer<> StickersListWidget::checkForHide() const {
object_ptr<TabbedSelector::InnerFooter> StickersListWidget::createFooter() {
Expects(_footer == nullptr);
auto result = object_ptr<Footer>(this);
auto result = object_ptr<Footer>(this, !_isMasks);
_footer = result;
_footer->openSettingsRequests(
) | rpl::start_with_next([=] {
const auto onlyFeatured = _footer->hasOnlyFeaturedSets();
Ui::show(Box<StickersBox>(
controller(),
(onlyFeatured
? StickersBox::Section::Featured
: _isMasks
? StickersBox::Section::Masks
: StickersBox::Section::Installed),
onlyFeatured ? false : _isMasks),
Ui::LayerOption::KeepOther);
}, _footer->lifetime());
return result;
}
@@ -1531,6 +1584,8 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
toColumn = _columnCount - toColumn;
}
_pathGradient->startFrame(0, width(), width() / 2);
auto &sets = shownSets();
auto selectedSticker = std::get_if<OverSticker>(&_selected);
auto selectedButton = std::get_if<OverButton>(!v::is_null(_pressed)
@@ -1889,6 +1944,7 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section,
h = std::max(qRound(coef * document->dimensions.height()), 1);
}
auto ppos = pos + QPoint((_singleSize.width() - w) / 2, (_singleSize.height() - h) / 2);
if (sticker.animated && sticker.animated->ready()) {
auto request = Lottie::FrameRequest();
request.box = boundingBoxSize() * cIntRetinaFactor();
@@ -1918,6 +1974,12 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section,
if (sticker.savedFrame.isNull()) {
sticker.savedFrame = pixmap;
}
} else {
PaintStickerThumbnailPath(
p,
media.get(),
QRect(ppos, QSize{ w, h }),
_pathGradient.get());
}
}
@@ -2363,12 +2425,12 @@ void StickersListWidget::refreshStickers() {
void StickersListWidget::refreshMySets() {
auto wasSets = base::take(_mySets);
_favedStickersMap.clear();
_mySets.reserve(session().data().stickers().setsOrder().size() + 3);
_mySets.reserve(defaultSetsOrder().size() + 3);
refreshFavedStickers();
refreshRecentStickers(false);
refreshMegagroupStickers(GroupStickersPlace::Visible);
for (const auto setId : session().data().stickers().setsOrder()) {
for (const auto setId : defaultSetsOrder()) {
const auto externalLayout = false;
appendSet(_mySets, setId, externalLayout, AppendSkip::Archived);
}
@@ -2441,7 +2503,9 @@ void StickersListWidget::refreshSearchIndex() {
}
void StickersListWidget::refreshSettingsVisibility() {
const auto visible = (_section == Section::Stickers) && _mySets.empty();
const auto visible = (_section == Section::Stickers)
&& _mySets.empty()
&& !_isMasks;
_settings->setVisible(visible);
}
@@ -2519,9 +2583,15 @@ auto StickersListWidget::collectRecentStickers() -> std::vector<Sticker> {
auto result = std::vector<Sticker>();
const auto &sets = session().data().stickers().sets();
const auto &recent = session().data().stickers().getRecentPack();
const auto customIt = sets.find(Data::Stickers::CustomSetId);
const auto cloudIt = sets.find(Data::Stickers::CloudRecentSetId);
const auto &recent = _isMasks
? RecentStickerPack()
: session().data().stickers().getRecentPack();
const auto customIt = _isMasks
? sets.cend()
: sets.find(Data::Stickers::CustomSetId);
const auto cloudIt = sets.find(_isMasks
? Data::Stickers::CloudRecentAttachedSetId
: Data::Stickers::CloudRecentSetId);
const auto customCount = (customIt != sets.cend())
? customIt->second->stickers.size()
: 0;
@@ -2607,6 +2677,9 @@ void StickersListWidget::refreshRecentStickers(bool performResize) {
}
void StickersListWidget::refreshFavedStickers() {
if (_isMasks) {
return;
}
clearSelection();
const auto &sets = session().data().stickers().sets();
const auto it = sets.find(Data::Stickers::FavedSetId);
@@ -2634,7 +2707,7 @@ void StickersListWidget::refreshFavedStickers() {
}
void StickersListWidget::refreshMegagroupStickers(GroupStickersPlace place) {
if (!_megagroupSet) {
if (!_megagroupSet || _isMasks) {
return;
}
auto canEdit = _megagroupSet->canEditStickers();
@@ -2720,7 +2793,7 @@ void StickersListWidget::refreshMegagroupStickers(GroupStickersPlace place) {
std::vector<StickerIcon> StickersListWidget::fillIcons() {
auto result = std::vector<StickerIcon>();
result.reserve(_mySets.size() + 1);
if (!_officialSets.empty()) {
if (!_officialSets.empty() && !_isMasks) {
result.emplace_back(Data::Stickers::FeaturedSetId);
}
@@ -2837,7 +2910,8 @@ bool StickersListWidget::setHasTitle(const Set &set) const {
if (set.id == Data::Stickers::FavedSetId) {
return false;
} else if (set.id == Data::Stickers::RecentSetId) {
return !_mySets.empty() && _mySets[0].id == Data::Stickers::FavedSetId;
return !_mySets.empty()
&& (_isMasks || (_mySets[0].id == Data::Stickers::FavedSetId));
}
return true;
}
@@ -3101,53 +3175,92 @@ void StickersListWidget::removeMegagroupSet(bool locally) {
void StickersListWidget::removeSet(uint64 setId) {
const auto &sets = session().data().stickers().sets();
const auto it = sets.find(setId);
if (it != sets.cend()) {
const auto set = it->second.get();
_removingSetId = set->id;
auto text = tr::lng_stickers_remove_pack(tr::now, lt_sticker_pack, set->title);
controller()->show(Box<ConfirmBox>(text, tr::lng_stickers_remove_pack_confirm(tr::now), crl::guard(this, [=] {
Ui::hideLayer();
const auto &sets = session().data().stickers().sets();
const auto it = sets.find(_removingSetId);
if (it != sets.cend()) {
const auto set = it->second.get();
if (set->id && set->access) {
_api.request(MTPmessages_UninstallStickerSet(MTP_inputStickerSetID(MTP_long(set->id), MTP_long(set->access)))).send();
} else if (!set->shortName.isEmpty()) {
_api.request(MTPmessages_UninstallStickerSet(MTP_inputStickerSetShortName(MTP_string(set->shortName)))).send();
}
auto writeRecent = false;
auto &recent = session().data().stickers().getRecentPack();
for (auto i = recent.begin(); i != recent.cend();) {
if (set->stickers.indexOf(i->first) >= 0) {
i = recent.erase(i);
writeRecent = true;
} else {
++i;
}
}
set->flags &= ~MTPDstickerSet::Flag::f_installed_date;
set->installDate = TimeId(0);
//
// Set can be in search results.
//
//if (!(set->flags & MTPDstickerSet_ClientFlag::f_featured)
// && !(set->flags & MTPDstickerSet_ClientFlag::f_special)) {
// sets.erase(it);
//}
int removeIndex = session().data().stickers().setsOrder().indexOf(_removingSetId);
if (removeIndex >= 0) session().data().stickers().setsOrderRef().removeAt(removeIndex);
refreshStickers();
session().local().writeInstalledStickers();
if (writeRecent) session().saveSettings();
}
_removingSetId = 0;
_checkForHide.fire({});
}), crl::guard(this, [=] {
_removingSetId = 0;
_checkForHide.fire({});
})));
if (it == sets.cend()) {
return;
}
const auto set = it->second.get();
_removingSetId = set->id;
const auto text = tr::lng_stickers_remove_pack(
tr::now,
lt_sticker_pack,
set->title);
const auto confirm = tr::lng_stickers_remove_pack_confirm(tr::now);
controller()->show(Box<ConfirmBox>(text, confirm, crl::guard(this, [=](
Fn<void()> &&close) {
close();
const auto &sets = session().data().stickers().sets();
const auto it = sets.find(_removingSetId);
if (it != sets.cend()) {
const auto set = it->second.get();
if (set->id && set->access) {
_api.request(MTPmessages_UninstallStickerSet(
MTP_inputStickerSetID(
MTP_long(set->id),
MTP_long(set->access)))
).send();
} else if (!set->shortName.isEmpty()) {
_api.request(MTPmessages_UninstallStickerSet(
MTP_inputStickerSetShortName(
MTP_string(set->shortName)))
).send();
}
auto writeRecent = false;
auto &recent = session().data().stickers().getRecentPack();
for (auto i = recent.begin(); i != recent.cend();) {
if (set->stickers.indexOf(i->first) >= 0) {
i = recent.erase(i);
writeRecent = true;
} else {
++i;
}
}
set->flags &= ~MTPDstickerSet::Flag::f_installed_date;
set->installDate = TimeId(0);
//
// Set can be in search results.
//
//if (!(set->flags & MTPDstickerSet_ClientFlag::f_featured)
// && !(set->flags & MTPDstickerSet_ClientFlag::f_special)) {
// sets.erase(it);
//}
const auto removeIndex = defaultSetsOrder().indexOf(
_removingSetId);
if (removeIndex >= 0) {
defaultSetsOrderRef().removeAt(removeIndex);
}
refreshStickers();
if (set->flags & MTPDstickerSet::Flag::f_masks) {
session().local().writeInstalledMasks();
} else {
session().local().writeInstalledStickers();
}
if (writeRecent) {
session().saveSettings();
}
session().data().stickers().notifyUpdated();
}
_removingSetId = 0;
_checkForHide.fire({});
}), crl::guard(this, [=] {
_removingSetId = 0;
_checkForHide.fire({});
})), Ui::LayerOption::KeepOther);
}
const Data::StickersSetsOrder &StickersListWidget::defaultSetsOrder() const {
return !_isMasks
? session().data().stickers().setsOrder()
: session().data().stickers().maskSetsOrder();
}
Data::StickersSetsOrder &StickersListWidget::defaultSetsOrderRef() {
return !_isMasks
? session().data().stickers().setsOrderRef()
: session().data().stickers().maskSetsOrderRef();
}
bool StickersListWidget::mySetsEmpty() const {
return _mySets.empty();
}
StickersListWidget::~StickersListWidget() = default;

View File

@@ -25,6 +25,7 @@ class LinkButton;
class PopupMenu;
class RippleAnimation;
class BoxContent;
class PathShiftGradient;
} // namespace Ui
namespace Lottie {
@@ -48,7 +49,8 @@ class StickersListWidget
public:
StickersListWidget(
QWidget *parent,
not_null<Window::SessionController*> controller);
not_null<Window::SessionController*> controller,
bool masks = false);
Main::Session &session() const;
@@ -87,6 +89,8 @@ public:
not_null<Ui::PopupMenu*> menu,
SendMenu::Type type) override;
bool mySetsEmpty() const;
~StickersListWidget();
protected:
@@ -299,6 +303,9 @@ private:
void refreshMegagroupSetGeometry();
QRect megagroupSetButtonRectFinal() const;
const Data::StickersSetsOrder &defaultSetsOrder() const;
Data::StickersSetsOrder &defaultSetsOrderRef();
enum class AppendSkip {
None,
Archived,
@@ -348,6 +355,7 @@ private:
int _officialOffset = 0;
Section _section = Section::Stickers;
const bool _isMasks;
bool _displayingSet = false;
uint64 _removingSetId = 0;
@@ -361,6 +369,8 @@ private:
OverState _pressed;
QPoint _lastMousePosition;
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
Ui::Text::String _megagroupSetAbout;
QString _megagroupSetButtonText;
int _megagroupSetButtonTextWidth = 0;

View File

@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "storage/cache/storage_cache_database.h"
#include "ui/effects/path_shift_gradient.h"
#include "main/main_session.h"
namespace ChatHelpers {
@@ -188,4 +189,50 @@ std::unique_ptr<Lottie::SinglePlayer> LottieThumbnail(
box);
}
bool PaintStickerThumbnailPath(
QPainter &p,
not_null<Data::DocumentMedia*> media,
QRect target,
QLinearGradient *gradient) {
const auto &path = media->thumbnailPath();
const auto dimensions = media->owner()->dimensions;
if (path.isEmpty() || dimensions.isEmpty() || target.isEmpty()) {
return false;
}
p.save();
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.translate(target.topLeft());
if (gradient) {
const auto scale = dimensions.width() / float64(target.width());
const auto shift = p.worldTransform().dx();
gradient->setStart((gradient->start().x() - shift) * scale, 0);
gradient->setFinalStop(
(gradient->finalStop().x() - shift) * scale,
0);
p.setBrush(*gradient);
}
p.scale(
target.width() / float64(dimensions.width()),
target.height() / float64(dimensions.height()));
p.drawPath(path);
p.restore();
return true;
}
bool PaintStickerThumbnailPath(
QPainter &p,
not_null<Data::DocumentMedia*> media,
QRect target,
not_null<Ui::PathShiftGradient*> gradient) {
return gradient->paint([&](const Ui::PathShiftGradient::Background &bg) {
if (const auto color = std::get_if<style::color>(&bg)) {
p.setBrush(*color);
return PaintStickerThumbnailPath(p, media, target);
}
const auto gradient = v::get<QLinearGradient*>(bg);
return PaintStickerThumbnailPath(p, media, target, gradient);
});
}
} // namespace ChatHelpers

View File

@@ -26,6 +26,10 @@ namespace Main {
class Session;
} // namespace Main
namespace Ui {
class PathShiftGradient;
} // namespace Ui
namespace Data {
class DocumentMedia;
class StickersSetThumbnailView;
@@ -71,4 +75,16 @@ enum class StickerLottieSize : uchar {
QSize box,
std::shared_ptr<Lottie::FrameRenderer> renderer = nullptr);
bool PaintStickerThumbnailPath(
QPainter &p,
not_null<Data::DocumentMedia*> media,
QRect target,
QLinearGradient *gradient = nullptr);
bool PaintStickerThumbnailPath(
QPainter &p,
not_null<Data::DocumentMedia*> media,
QRect target,
not_null<Ui::PathShiftGradient*> gradient);
} // namespace ChatHelpers

View File

@@ -256,8 +256,12 @@ void TabbedSelector::SlideAnimation::paintFrame(QPainter &p, float64 dt, float64
p.drawImage(outerLeft / cIntRetinaFactor(), outerTop / cIntRetinaFactor(), _frame, outerLeft, outerTop, outerRight - outerLeft, outerBottom - outerTop);
}
TabbedSelector::Tab::Tab(SelectorTab type, object_ptr<Inner> widget)
TabbedSelector::Tab::Tab(
SelectorTab type,
int index,
object_ptr<Inner> widget)
: _type(type)
, _index(index)
, _widget(std::move(widget))
, _weak(_widget)
, _footer(_widget ? _widget->createFooter() : nullptr) {
@@ -292,33 +296,52 @@ TabbedSelector::TabbedSelector(
, _topShadow(full() ? object_ptr<Ui::PlainShadow>(this) : nullptr)
, _bottomShadow(this)
, _scroll(this, st::emojiScroll)
, _tabs { {
createTab(SelectorTab::Emoji),
createTab(SelectorTab::Stickers),
createTab(SelectorTab::Gifs),
} }
, _tabs([&] {
std::vector<Tab> tabs;
if (full()) {
tabs.reserve(3);
tabs.push_back(createTab(SelectorTab::Emoji, 0));
tabs.push_back(createTab(SelectorTab::Stickers, 1));
tabs.push_back(createTab(SelectorTab::Gifs, 2));
} else if (mediaEditor()) {
tabs.reserve(2);
tabs.push_back(createTab(SelectorTab::Stickers, 0));
tabs.push_back(createTab(SelectorTab::Masks, 1));
} else {
tabs.reserve(1);
tabs.push_back(createTab(SelectorTab::Emoji, 0));
}
return tabs;
}())
, _currentTabType(full()
? session().settings().selectorTab()
: SelectorTab::Emoji) {
: mediaEditor()
? SelectorTab::Stickers
: SelectorTab::Emoji)
, _hasEmojiTab(ranges::contains(_tabs, SelectorTab::Emoji, &Tab::type))
, _hasStickersTab(ranges::contains(_tabs, SelectorTab::Stickers, &Tab::type))
, _hasGifsTab(ranges::contains(_tabs, SelectorTab::Gifs, &Tab::type))
, _hasMasksTab(ranges::contains(_tabs, SelectorTab::Masks, &Tab::type))
, _tabbed(_tabs.size() > 1) {
resize(st::emojiPanWidth, st::emojiPanMaxHeight);
for (auto &tab : _tabs) {
if (!tab.widget()) {
continue;
}
tab.footer()->hide();
tab.widget()->hide();
}
createTabsSlider();
if (tabbed()) {
createTabsSlider();
}
setWidgetToScrollArea();
_bottomShadow->setGeometry(0, _scroll->y() + _scroll->height() - st::lineWidth, width(), st::lineWidth);
_bottomShadow->setGeometry(
0,
_scroll->y() + _scroll->height() - st::lineWidth,
width(),
st::lineWidth);
for (auto &tab : _tabs) {
const auto widget = tab.widget();
if (!widget) {
continue;
}
widget->scrollToRequests(
) | rpl::start_with_next([=, tab = &tab](int y) {
@@ -338,7 +361,7 @@ TabbedSelector::TabbedSelector(
}
rpl::merge(
(full()
(hasStickersTab()
? stickers()->scrollUpdated() | rpl::map_to(0)
: rpl::never<int>() | rpl::type_erased()),
_scroll->scrollTopChanges()
@@ -346,13 +369,15 @@ TabbedSelector::TabbedSelector(
handleScroll();
}, lifetime());
if (full()) {
if (_topShadow) {
_topShadow->raise();
}
_bottomShadow->raise();
if (full()) {
if (_tabsSlider) {
_tabsSlider->raise();
}
if (hasStickersTab() || hasGifsTab()) {
session().changes().peerUpdates(
Data::PeerUpdate::Flag::Rights
) | rpl::filter([=](const Data::PeerUpdate &update) {
@@ -360,11 +385,12 @@ TabbedSelector::TabbedSelector(
}) | rpl::start_with_next([=] {
checkRestrictedPeer();
}, lifetime());
}
session().api().stickerSetInstalled(
) | rpl::start_with_next([this](uint64 setId) {
_tabsSlider->setActiveSection(
static_cast<int>(SelectorTab::Stickers));
if (hasStickersTab()) {
session().data().stickers().stickerSetInstalled(
) | rpl::start_with_next([=](uint64 setId) {
_tabsSlider->setActiveSection(indexByType(SelectorTab::Stickers));
stickers()->showStickerSet(setId);
_showRequests.fire({});
}, lifetime());
@@ -387,11 +413,8 @@ Main::Session &TabbedSelector::session() const {
return _controller->session();
}
TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type) {
TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type, int index) {
auto createWidget = [&]() -> object_ptr<Inner> {
if (!full() && type != SelectorTab::Emoji) {
return { nullptr };
}
switch (type) {
case SelectorTab::Emoji:
return object_ptr<EmojiListWidget>(this, _controller);
@@ -399,52 +422,94 @@ TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type) {
return object_ptr<StickersListWidget>(this, _controller);
case SelectorTab::Gifs:
return object_ptr<GifsListWidget>(this, _controller);
case SelectorTab::Masks:
return object_ptr<StickersListWidget>(this, _controller, true);
}
Unexpected("Type in TabbedSelector::createTab.");
};
return Tab{ type, createWidget() };
return Tab{ type, index, createWidget() };
}
bool TabbedSelector::full() const {
return (_mode == Mode::Full);
}
bool TabbedSelector::mediaEditor() const {
return (_mode == Mode::MediaEditor);
}
bool TabbedSelector::tabbed() const {
return _tabbed;
}
bool TabbedSelector::hasEmojiTab() const {
return _hasEmojiTab;
}
bool TabbedSelector::hasStickersTab() const {
return _hasStickersTab;
}
bool TabbedSelector::hasGifsTab() const {
return _hasGifsTab;
}
bool TabbedSelector::hasMasksTab() const {
return _hasMasksTab;
}
rpl::producer<EmojiPtr> TabbedSelector::emojiChosen() const {
return emoji()->chosen();
}
rpl::producer<TabbedSelector::FileChosen> TabbedSelector::fileChosen() const {
return full()
? rpl::merge(stickers()->chosen(), gifs()->fileChosen())
: rpl::never<TabbedSelector::FileChosen>() | rpl::type_erased();
auto never = rpl::never<TabbedSelector::FileChosen>(
) | rpl::type_erased();
return rpl::merge(
hasStickersTab() ? stickers()->chosen() : never,
hasGifsTab() ? gifs()->fileChosen() : never,
hasMasksTab() ? masks()->chosen() : never);
}
auto TabbedSelector::photoChosen() const
-> rpl::producer<TabbedSelector::PhotoChosen>{
return full() ? gifs()->photoChosen() : nullptr;
return hasGifsTab() ? gifs()->photoChosen() : nullptr;
}
auto TabbedSelector::inlineResultChosen() const
-> rpl::producer<InlineChosen> {
return full() ? gifs()->inlineResultChosen() : nullptr;
return hasGifsTab() ? gifs()->inlineResultChosen() : nullptr;
}
rpl::producer<> TabbedSelector::cancelled() const {
return full() ? gifs()->cancelRequests() : nullptr;
return hasGifsTab() ? gifs()->cancelRequests() : nullptr;
}
rpl::producer<> TabbedSelector::checkForHide() const {
return full() ? stickers()->checkForHide() : nullptr;
auto never = rpl::never<>();
return rpl::merge(
hasStickersTab() ? stickers()->checkForHide() : never,
hasMasksTab() ? masks()->checkForHide() : never);
}
rpl::producer<> TabbedSelector::slideFinished() const {
return _slideFinished.events();
}
void TabbedSelector::updateTabsSliderGeometry() {
if (!_tabsSlider) {
return;
}
const auto w = mediaEditor() && hasMasksTab() && masks()->mySetsEmpty()
? width() / 2
: width();
_tabsSlider->resizeToWidth(w);
_tabsSlider->moveToLeft(0, 0);
}
void TabbedSelector::resizeEvent(QResizeEvent *e) {
if (full()) {
_tabsSlider->resizeToWidth(width());
_tabsSlider->moveToLeft(0, 0);
updateTabsSliderGeometry();
if (_topShadow && _tabsSlider) {
_topShadow->setGeometry(
_tabsSlider->x(),
_tabsSlider->bottomNoMargins() - st::lineWidth,
@@ -476,14 +541,15 @@ void TabbedSelector::resizeEvent(QResizeEvent *e) {
updateInnerGeometry();
updateScrollGeometry();
}
_bottomShadow->setGeometry(0, _scroll->y() + _scroll->height() - st::lineWidth, width(), st::lineWidth);
_bottomShadow->setGeometry(
0,
_scroll->y() + _scroll->height() - st::lineWidth,
width(),
st::lineWidth);
updateRestrictedLabelGeometry();
_footerTop = height() - st::emojiFooterHeight;
for (auto &tab : _tabs) {
if (!tab.widget()) {
continue;
}
tab.footer()->resizeToWidth(width());
tab.footer()->moveToLeft(0, _footerTop);
}
@@ -521,14 +587,22 @@ void TabbedSelector::paintEvent(QPaintEvent *e) {
void TabbedSelector::paintSlideFrame(Painter &p) {
if (_roundRadius > 0) {
if (full()) {
auto topPart = QRect(0, 0, width(), _tabsSlider->height() + _roundRadius);
Ui::FillRoundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop | RectPart::NoTopBottom);
} else {
auto topPart = QRect(0, 0, width(), 3 * _roundRadius);
Ui::FillRoundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop);
}
} else if (full()) {
const auto topPart = QRect(
0,
0,
width(),
_tabsSlider
? _tabsSlider->height() + _roundRadius
: 3 * _roundRadius);
Ui::FillRoundRect(
p,
topPart,
st::emojiPanBg,
ImageRoundRadius::Small,
tabbed()
? RectPart::FullTop | RectPart::NoTopBottom
: RectPart::FullTop);
} else if (_tabsSlider) {
p.fillRect(0, 0, width(), _tabsSlider->height(), st::emojiPanBg);
}
auto slideDt = _a_slide.value(1.);
@@ -536,21 +610,39 @@ void TabbedSelector::paintSlideFrame(Painter &p) {
}
void TabbedSelector::paintContent(Painter &p) {
auto &bottomBg = hasSectionIcons() ? st::emojiPanCategories : st::emojiPanBg;
auto &bottomBg = hasSectionIcons()
? st::emojiPanCategories
: st::emojiPanBg;
if (_roundRadius > 0) {
if (full()) {
auto topPart = QRect(0, 0, width(), _tabsSlider->height() + _roundRadius);
Ui::FillRoundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop | RectPart::NoTopBottom);
} else {
auto topPart = QRect(0, 0, width(), 3 * _roundRadius);
Ui::FillRoundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop);
}
const auto topPart = QRect(
0,
0,
width(),
_tabsSlider
? _tabsSlider->height() + _roundRadius
: 3 * _roundRadius);
Ui::FillRoundRect(
p,
topPart,
st::emojiPanBg,
ImageRoundRadius::Small,
tabbed()
? RectPart::FullTop | RectPart::NoTopBottom
: RectPart::FullTop);
auto bottomPart = QRect(0, _footerTop - _roundRadius, width(), st::emojiFooterHeight + _roundRadius);
auto bottomParts = RectPart::NoTopBottom | RectPart::FullBottom;
Ui::FillRoundRect(p, bottomPart, bottomBg, ImageRoundRadius::Small, bottomParts);
const auto bottomPart = QRect(
0,
_footerTop - _roundRadius,
width(),
st::emojiFooterHeight + _roundRadius);
Ui::FillRoundRect(
p,
bottomPart,
bottomBg,
ImageRoundRadius::Small,
RectPart::NoTopBottom | RectPart::FullBottom);
} else {
if (full()) {
if (_tabsSlider) {
p.fillRect(0, 0, width(), _tabsSlider->height(), st::emojiPanBg);
}
p.fillRect(0, _footerTop, width(), st::emojiFooterHeight, bottomBg);
@@ -561,17 +653,27 @@ void TabbedSelector::paintContent(Painter &p) {
if (_restrictedLabel) {
p.fillRect(0, sidesTop, width(), sidesHeight, st::emojiPanBg);
} else {
p.fillRect(myrtlrect(width() - st::emojiScroll.width, sidesTop, st::emojiScroll.width, sidesHeight), st::emojiPanBg);
p.fillRect(myrtlrect(0, sidesTop, st::roundRadiusSmall, sidesHeight), st::emojiPanBg);
p.fillRect(
myrtlrect(
width() - st::emojiScroll.width,
sidesTop,
st::emojiScroll.width,
sidesHeight),
st::emojiPanBg);
p.fillRect(
myrtlrect(0, sidesTop, st::roundRadiusSmall, sidesHeight),
st::emojiPanBg);
}
}
int TabbedSelector::marginTop() const {
return full() ? (_tabsSlider->height() - st::lineWidth) : _roundRadius;
return _tabsSlider
? (_tabsSlider->height() - st::lineWidth)
: _roundRadius;
}
int TabbedSelector::scrollTop() const {
return full() ? marginTop() : 0;
return tabbed() ? marginTop() : 0;
}
int TabbedSelector::marginBottom() const {
@@ -579,19 +681,31 @@ int TabbedSelector::marginBottom() const {
}
void TabbedSelector::refreshStickers() {
if (!full()) {
return;
if (hasStickersTab()) {
stickers()->refreshStickers();
if (isHidden() || _currentTabType != SelectorTab::Stickers) {
stickers()->preloadImages();
}
}
stickers()->refreshStickers();
if (isHidden() || _currentTabType != SelectorTab::Stickers) {
stickers()->preloadImages();
if (hasMasksTab()) {
const auto masksList = masks();
masksList->refreshStickers();
if (isHidden() || _currentTabType != SelectorTab::Masks) {
masksList->preloadImages();
}
fillTabsSliderSections();
updateTabsSliderGeometry();
if (hasStickersTab() && masksList->mySetsEmpty()) {
_tabsSlider->setActiveSection(indexByType(SelectorTab::Stickers));
}
}
}
bool TabbedSelector::preventAutoHide() const {
return full()
? (stickers()->preventAutoHide() || hasMenu())
: false;
return (hasStickersTab() ? stickers()->preventAutoHide() : false)
|| (hasMasksTab() ? masks()->preventAutoHide() : false)
|| hasMenu();
}
bool TabbedSelector::hasMenu() const {
@@ -603,13 +717,17 @@ QImage TabbedSelector::grabForAnimation() {
auto slideAnimation = base::take(_a_slide);
showAll();
if (full()) {
if (_topShadow) {
_topShadow->hide();
}
if (_tabsSlider) {
_tabsSlider->hide();
}
Ui::SendPendingMoveResizeEvents(this);
auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
auto result = QImage(
size() * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
result.fill(Qt::transparent);
render(&result);
@@ -630,9 +748,6 @@ QRect TabbedSelector::floatPlayerAvailableRect() const {
void TabbedSelector::hideFinished() {
for (auto &tab : _tabs) {
if (!tab.widget()) {
continue;
}
tab.widget()->panelHideFinished();
}
_a_slide.stop();
@@ -640,9 +755,12 @@ void TabbedSelector::hideFinished() {
}
void TabbedSelector::showStarted() {
if (full()) {
if (hasStickersTab()) {
session().api().updateStickers();
}
if (hasMasksTab()) {
session().api().updateMasks();
}
currentTab()->widget()->refreshRecent();
currentTab()->widget()->preloadImages();
_a_slide.stop();
@@ -670,13 +788,14 @@ void TabbedSelector::afterShown() {
}
void TabbedSelector::setCurrentPeer(PeerData *peer) {
if (!full()) {
return;
if (hasGifsTab()) {
gifs()->setInlineQueryPeer(peer);
}
gifs()->setInlineQueryPeer(peer);
_currentPeer = peer;
checkRestrictedPeer();
stickers()->showMegagroupSet(peer ? peer->asMegagroup() : nullptr);
if (hasStickersTab()) {
stickers()->showMegagroupSet(peer ? peer->asMegagroup() : nullptr);
}
}
void TabbedSelector::checkRestrictedPeer() {
@@ -730,16 +849,20 @@ void TabbedSelector::showAll() {
_scroll->show();
_bottomShadow->setVisible(_currentTabType == SelectorTab::Gifs);
}
if (full()) {
if (_topShadow) {
_topShadow->show();
}
if (_tabsSlider) {
_tabsSlider->show();
}
}
void TabbedSelector::hideForSliding() {
hideChildren();
if (full()) {
if (_topShadow) {
_topShadow->show();
}
if (_tabsSlider) {
_tabsSlider->show();
}
currentTab()->widget()->clearSelection();
@@ -753,48 +876,70 @@ void TabbedSelector::handleScroll() {
void TabbedSelector::setRoundRadius(int radius) {
_roundRadius = radius;
if (full()) {
if (_tabsSlider) {
_tabsSlider->setRippleTopRoundRadius(_roundRadius);
}
}
void TabbedSelector::createTabsSlider() {
if (!full()) {
return;
}
_tabsSlider.create(this, st::emojiTabs);
auto sections = QStringList();
sections.push_back(tr::lng_switch_emoji(tr::now).toUpper());
sections.push_back(tr::lng_switch_stickers(tr::now).toUpper());
sections.push_back(tr::lng_switch_gifs(tr::now).toUpper());
_tabsSlider->setSections(sections);
fillTabsSliderSections();
_tabsSlider->setActiveSectionFast(static_cast<int>(_currentTabType));
_tabsSlider->setActiveSectionFast(indexByType(_currentTabType));
_tabsSlider->sectionActivated(
) | rpl::start_with_next([=] {
switchTab();
}, lifetime());
}
void TabbedSelector::fillTabsSliderSections() {
if (!_tabsSlider) {
return;
}
const auto sections = ranges::views::all(
_tabs
) | ranges::views::filter([&](const Tab &tab) {
return (tab.type() == SelectorTab::Masks)
? !masks()->mySetsEmpty()
: true;
}) | ranges::views::transform([&](const Tab &tab) {
return [&] {
switch (tab.type()) {
case SelectorTab::Emoji:
return tr::lng_switch_emoji;
case SelectorTab::Stickers:
return tr::lng_switch_stickers;
case SelectorTab::Gifs:
return tr::lng_switch_gifs;
case SelectorTab::Masks:
return tr::lng_switch_masks;
}
Unexpected("SelectorTab value in fillTabsSliderSections.");
}()(tr::now).toUpper();
}) | ranges::to_vector;
_tabsSlider->setSections(sections);
}
bool TabbedSelector::hasSectionIcons() const {
return (_currentTabType != SelectorTab::Gifs) && !_restrictedLabel;
}
void TabbedSelector::switchTab() {
Expects(full());
Expects(tabbed());
auto tab = _tabsSlider->activeSection();
Assert(tab >= 0 && tab < Tab::kCount);
auto newTabType = static_cast<SelectorTab>(tab);
const auto tab = _tabsSlider->activeSection();
Assert(tab >= 0 && tab < _tabs.size());
const auto newTabType = typeByIndex(tab);
if (_currentTabType == newTabType) {
_scroll->scrollToY(0);
return;
}
auto wasSectionIcons = hasSectionIcons();
auto wasTab = _currentTabType;
const auto wasSectionIcons = hasSectionIcons();
const auto wasTab = _currentTabType;
const auto wasIndex = indexByType(_currentTabType);
currentTab()->saveScrollTop();
beforeHiding();
@@ -817,21 +962,38 @@ void TabbedSelector::switchTab() {
auto nowCache = grabForAnimation();
auto direction = (wasTab > _currentTabType) ? SlideAnimation::Direction::LeftToRight : SlideAnimation::Direction::RightToLeft;
auto direction = (wasIndex > indexByType(_currentTabType))
? SlideAnimation::Direction::LeftToRight
: SlideAnimation::Direction::RightToLeft;
if (direction == SlideAnimation::Direction::LeftToRight) {
std::swap(wasCache, nowCache);
}
_slideAnimation = std::make_unique<SlideAnimation>();
auto slidingRect = QRect(0, _scroll->y() * cIntRetinaFactor(), width() * cIntRetinaFactor(), (height() - _scroll->y()) * cIntRetinaFactor());
_slideAnimation->setFinalImages(direction, std::move(wasCache), std::move(nowCache), slidingRect, wasSectionIcons);
_slideAnimation->setCornerMasks(Images::CornersMask(ImageRoundRadius::Small));
const auto slidingRect = QRect(
0,
_scroll->y() * cIntRetinaFactor(),
width() * cIntRetinaFactor(),
(height() - _scroll->y()) * cIntRetinaFactor());
_slideAnimation->setFinalImages(
direction,
std::move(wasCache),
std::move(nowCache),
slidingRect,
wasSectionIcons);
_slideAnimation->setCornerMasks(
Images::CornersMask(ImageRoundRadius::Small));
_slideAnimation->start();
hideForSliding();
getTab(wasTab)->widget()->hideFinished();
getTab(wasIndex)->widget()->hideFinished();
_a_slide.start([this] { update(); }, 0., 1., st::emojiPanSlideDuration, anim::linear);
_a_slide.start(
[=] { update(); },
0.,
1.,
st::emojiPanSlideDuration,
anim::linear);
update();
if (full()) {
@@ -841,19 +1003,31 @@ void TabbedSelector::switchTab() {
}
not_null<EmojiListWidget*> TabbedSelector::emoji() const {
return static_cast<EmojiListWidget*>(getTab(SelectorTab::Emoji)->widget());
Expects(hasEmojiTab());
return static_cast<EmojiListWidget*>(
getTab(indexByType(SelectorTab::Emoji))->widget());
}
not_null<StickersListWidget*> TabbedSelector::stickers() const {
Expects(full());
Expects(hasStickersTab());
return static_cast<StickersListWidget*>(getTab(SelectorTab::Stickers)->widget());
return static_cast<StickersListWidget*>(
getTab(indexByType(SelectorTab::Stickers))->widget());
}
not_null<GifsListWidget*> TabbedSelector::gifs() const {
Expects(full());
Expects(hasGifsTab());
return static_cast<GifsListWidget*>(getTab(SelectorTab::Gifs)->widget());
return static_cast<GifsListWidget*>(
getTab(indexByType(SelectorTab::Gifs))->widget());
}
not_null<StickersListWidget*> TabbedSelector::masks() const {
Expects(hasMasksTab());
return static_cast<StickersListWidget*>(
getTab(indexByType(SelectorTab::Masks))->widget());
}
void TabbedSelector::setWidgetToScrollArea() {
@@ -873,7 +1047,7 @@ void TabbedSelector::scrollToY(int y) {
_scroll->scrollToY(y);
// Qt render glitch workaround, shadow sometimes disappears if we just scroll to y.
if (full()) {
if (_topShadow) {
_topShadow->update();
}
}
@@ -894,6 +1068,40 @@ rpl::producer<> TabbedSelector::contextMenuRequested() const {
}) | rpl::to_empty;
}
SelectorTab TabbedSelector::typeByIndex(int index) const {
for (const auto &tab : _tabs) {
if (tab.index() == index) {
return tab.type();
}
}
Unexpected("Type in TabbedSelector::typeByIndex.");
}
int TabbedSelector::indexByType(SelectorTab type) const {
for (const auto &tab : _tabs) {
if (tab.type() == type) {
return tab.index();
}
}
Unexpected("Index in TabbedSelector::indexByType.");
}
not_null<TabbedSelector::Tab*> TabbedSelector::getTab(int index) {
return &(_tabs[index]);
}
not_null<const TabbedSelector::Tab*> TabbedSelector::getTab(int index) const {
return &_tabs[index];
}
not_null<TabbedSelector::Tab*> TabbedSelector::currentTab() {
return &_tabs[indexByType(_currentTabType)];
}
not_null<const TabbedSelector::Tab*> TabbedSelector::currentTab() const {
return &_tabs[indexByType(_currentTabType)];
}
TabbedSelector::Inner::Inner(
QWidget *parent,
not_null<Window::SessionController*> controller)
@@ -917,7 +1125,9 @@ void TabbedSelector::Inner::disableScroll(bool disabled) {
_disableScrollRequests.fire_copy(disabled);
}
void TabbedSelector::Inner::visibleTopBottomUpdated(int visibleTop, int visibleBottom) {
void TabbedSelector::Inner::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
_visibleTop = visibleTop;
_visibleBottom = visibleBottom;
}

View File

@@ -44,6 +44,7 @@ enum class SelectorTab {
Emoji,
Stickers,
Gifs,
Masks,
};
class EmojiListWidget;
@@ -63,7 +64,8 @@ public:
using InlineChosen = InlineBots::ResultSelected;
enum class Mode {
Full,
EmojiOnly
EmojiOnly,
MediaEditor,
};
TabbedSelector(
@@ -130,9 +132,7 @@ protected:
private:
class Tab {
public:
static constexpr auto kCount = 3;
Tab(SelectorTab type, object_ptr<Inner> widget);
Tab(SelectorTab type, int index, object_ptr<Inner> widget);
object_ptr<Inner> takeWidget();
void returnWidget(object_ptr<Inner> widget);
@@ -140,6 +140,9 @@ private:
SelectorTab type() const {
return _type;
}
int index() const {
return _index;
}
Inner *widget() const {
return _weak;
}
@@ -156,7 +159,8 @@ private:
}
private:
SelectorTab _type = SelectorTab::Emoji;
const SelectorTab _type;
const int _index;
object_ptr<Inner> _widget = { nullptr };
QPointer<Inner> _weak;
object_ptr<InnerFooter> _footer;
@@ -165,7 +169,13 @@ private:
};
bool full() const;
Tab createTab(SelectorTab type);
bool mediaEditor() const;
bool tabbed() const;
bool hasEmojiTab() const;
bool hasStickersTab() const;
bool hasGifsTab() const;
bool hasMasksTab() const;
Tab createTab(SelectorTab type, int index);
void paintSlideFrame(Painter &p);
void paintContent(Painter &p);
@@ -182,25 +192,25 @@ private:
void showAll();
void hideForSliding();
SelectorTab typeByIndex(int index) const;
int indexByType(SelectorTab type) const;
bool hasSectionIcons() const;
void setWidgetToScrollArea();
void createTabsSlider();
void fillTabsSliderSections();
void updateTabsSliderGeometry();
void switchTab();
not_null<Tab*> getTab(SelectorTab type) {
return &_tabs[static_cast<int>(type)];
}
not_null<const Tab*> getTab(SelectorTab type) const {
return &_tabs[static_cast<int>(type)];
}
not_null<Tab*> currentTab() {
return getTab(_currentTabType);
}
not_null<const Tab*> currentTab() const {
return getTab(_currentTabType);
}
not_null<Tab*> getTab(int index);
not_null<const Tab*> getTab(int index) const;
not_null<Tab*> currentTab();
not_null<const Tab*> currentTab() const;
not_null<EmojiListWidget*> emoji() const;
not_null<StickersListWidget*> stickers() const;
not_null<GifsListWidget*> gifs() const;
not_null<StickersListWidget*> masks() const;
const not_null<Window::SessionController*> _controller;
@@ -218,9 +228,15 @@ private:
object_ptr<Ui::PlainShadow> _bottomShadow;
object_ptr<Ui::ScrollArea> _scroll;
object_ptr<Ui::FlatLabel> _restrictedLabel = { nullptr };
std::array<Tab, Tab::kCount> _tabs;
std::vector<Tab> _tabs;
SelectorTab _currentTabType = SelectorTab::Emoji;
const bool _hasEmojiTab;
const bool _hasStickersTab;
const bool _hasGifsTab;
const bool _hasMasksTab;
const bool _tabbed;
base::unique_qptr<Ui::PopupMenu> _menu;
Fn<void(SelectorTab)> _afterShownCallback;

View File

@@ -58,6 +58,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_lock_widgets.h"
#include "history/history_location_manager.h"
#include "ui/widgets/tooltip.h"
#include "ui/gl/gl_detection.h"
#include "ui/image/image.h"
#include "ui/text/text_options.h"
#include "ui/emoji_config.h"
@@ -281,8 +282,7 @@ void Application::run() {
LOG(("Shortcuts Error: %1").arg(error));
}
if (!Platform::IsMac()
&& Ui::Integration::Instance().openglLastCheckFailed()) {
if (!Platform::IsMac() && Ui::GL::LastCrashCheckFailed()) {
showOpenGLCrashNotification();
}
@@ -297,14 +297,14 @@ void Application::run() {
void Application::showOpenGLCrashNotification() {
const auto enable = [=] {
Ui::GL::ForceDisable(false);
Ui::Integration::Instance().openglCheckFinish();
Ui::GL::CrashCheckFinish();
Core::App().settings().setDisableOpenGL(false);
Local::writeSettings();
App::restart();
};
const auto keepDisabled = [=] {
Ui::GL::ForceDisable(true);
Ui::Integration::Instance().openglCheckFinish();
Ui::GL::CrashCheckFinish();
Core::App().settings().setDisableOpenGL(true);
Local::writeSettings();
};

View File

@@ -135,6 +135,17 @@ std::map<int, const char*> BetaLogs() {
"- Several bug fixes.\n"
},
{
2008006,
"- Added a simple image editor. "
"Crop photos or highlight parts of screenshots before sending.\n"
"- Use Direct3D 9 backend in ANGLE by default (Windows).\n"
"- Fix \"Show in Finder\" not focusing the Finder window (macOS).\n"
"- Use GTK from a child process (Linux).\n"
},
};
};

View File

@@ -113,7 +113,8 @@ QByteArray Settings::serialize() const {
+ sizeof(qint64)
+ sizeof(qint32) * 2
+ Serialize::bytearraySize(windowPosition)
+ sizeof(qint32);
+ sizeof(qint32)
+ Serialize::bytearraySize(_photoEditorBrush);
for (const auto &[id, rating] : recentEmojiPreloadData) {
size += Serialize::stringSize(id) + sizeof(quint16);
}
@@ -123,7 +124,7 @@ QByteArray Settings::serialize() const {
}
size += sizeof(qint32) * 3
+ Serialize::bytearraySize(proxy)
+ sizeof(qint32);
+ sizeof(qint32) * 2;
auto result = QByteArray();
result.reserve(size);
@@ -211,11 +212,13 @@ QByteArray Settings::serialize() const {
stream << id << quint8(variant);
}
stream
<< qint32(_disableOpenGL ? 1 : 0)
<< qint32(0) // Old Disable OpenGL
<< qint32(_groupCallNoiseSuppression ? 1 : 0)
<< qint32(_workMode.current())
<< proxy
<< qint32(_hiddenGroupCallTooltips.value());
<< qint32(_hiddenGroupCallTooltips.value())
<< qint32(_disableOpenGL ? 1 : 0)
<< _photoEditorBrush;
}
return result;
}
@@ -296,6 +299,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
qint32 workMode = static_cast<qint32>(_workMode.current());
QByteArray proxy;
qint32 hiddenGroupCallTooltips = qint32(_hiddenGroupCallTooltips.value());
QByteArray photoEditorBrush = _photoEditorBrush;
stream >> themesAccentColors;
if (!stream.atEnd()) {
@@ -425,7 +429,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
}
}
if (!stream.atEnd()) {
stream >> disableOpenGL;
qint32 disableOpenGLOld;
stream >> disableOpenGLOld;
}
if (!stream.atEnd()) {
stream >> groupCallNoiseSuppression;
@@ -439,6 +444,12 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) {
stream >> hiddenGroupCallTooltips;
}
if (!stream.atEnd()) {
stream >> disableOpenGL;
}
if (!stream.atEnd()) {
stream >> photoEditorBrush;
}
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()"));
@@ -549,7 +560,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
_disableOpenGL = (disableOpenGL == 1);
if (!Platform::IsMac()) {
Ui::GL::ForceDisable(_disableOpenGL
|| Ui::Integration::Instance().openglLastCheckFailed());
|| Ui::GL::LastCrashCheckFailed());
}
_groupCallNoiseSuppression = (groupCallNoiseSuppression == 1);
const auto uncheckedWorkMode = static_cast<WorkMode>(workMode);
@@ -568,6 +579,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
? Tooltip::Microphone
: Tooltip(0));
}();
_photoEditorBrush = photoEditorBrush;
}
QString Settings::getSoundPath(const QString &key) const {

View File

@@ -434,6 +434,13 @@ public:
_videoPipGeometry = geometry;
}
[[nodiscard]] QByteArray photoEditorBrush() const {
return _photoEditorBrush;
}
void setPhotoEditorBrush(QByteArray brush) {
_photoEditorBrush = brush;
}
[[nodiscard]] float64 rememberedSongVolume() const {
return _rememberedSongVolume;
}
@@ -696,6 +703,8 @@ private:
bool _rememberedSoundNotifyFromTray = false;
bool _rememberedFlashBounceNotifyFromTray = false;
QByteArray _photoEditorBrush;
};
} // namespace Core

View File

@@ -19,7 +19,7 @@ public:
static std::unique_ptr<Launcher> Create(int argc, char *argv[]);
int exec();
virtual int exec();
QString argumentsString() const;
bool customWorkingDir() const;

View File

@@ -88,6 +88,10 @@ const auto kBadPrefix = u"http://"_q;
return cWorkingDir() + "tdata/opengl_crash_check";
}
[[nodiscard]] QString ANGLEBackendFilePath() {
return cWorkingDir() + "tdata/angle_backend";
}
} // namespace
void UiIntegration::postponeCall(FnMut<void()> &&callable) {
@@ -106,20 +110,12 @@ QString UiIntegration::emojiCacheFolder() {
return cWorkingDir() + "tdata/emoji";
}
void UiIntegration::openglCheckStart() {
auto f = QFile(OpenGLCheckFilePath());
if (f.open(QIODevice::WriteOnly)) {
f.write("1", 1);
f.close();
}
QString UiIntegration::openglCheckFilePath() {
return OpenGLCheckFilePath();
}
void UiIntegration::openglCheckFinish() {
QFile::remove(OpenGLCheckFilePath());
}
bool UiIntegration::openglLastCheckFailed() {
return OpenGLLastCheckFailed();
QString UiIntegration::angleBackendFilePath() {
return ANGLEBackendFilePath();
}
void UiIntegration::textActionsUpdated() {

View File

@@ -37,10 +37,8 @@ public:
void unregisterLeaveSubscription(not_null<QWidget*> widget) override;
QString emojiCacheFolder() override;
void openglCheckStart() override;
void openglCheckFinish() override;
bool openglLastCheckFailed() override;
QString openglCheckFilePath() override;
QString angleBackendFilePath() override;
void textActionsUpdated() override;
void activationFromTopPanel() override;

View File

@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
constexpr auto AppName = "Telegram Desktop"_cs;
constexpr auto AppFile = "Telegram"_cs;
constexpr auto AppVersion = 2008002;
constexpr auto AppVersionStr = "2.8.2";
constexpr auto AppBetaVersion = false;
constexpr auto AppVersion = 2008006;
constexpr auto AppVersionStr = "2.8.6";
constexpr auto AppBetaVersion = true;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -0,0 +1,78 @@
/*
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
template <typename IdsContainer>
class AbstractSparseIds {
public:
using Id = typename IdsContainer::value_type;
AbstractSparseIds() = default;
AbstractSparseIds(
const IdsContainer &ids,
std::optional<int> fullCount,
std::optional<int> skippedBefore,
std::optional<int> skippedAfter)
: _ids(ids)
, _fullCount(fullCount)
, _skippedBefore(skippedBefore)
, _skippedAfter(skippedAfter) {
}
std::optional<int> fullCount() const {
return _fullCount;
}
std::optional<int> skippedBefore() const {
return _skippedBefore;
}
std::optional<int> skippedAfter() const {
return _skippedAfter;
}
std::optional<int> indexOf(Id id) const {
const auto it = ranges::find(_ids, id);
if (it != _ids.end()) {
return (it - _ids.begin());
}
return std::nullopt;
}
int size() const {
return _ids.size();
}
Id operator[](int index) const {
Expects(index >= 0 && index < size());
return *(_ids.begin() + index);
}
std::optional<int> distance(Id a, Id b) const {
if (const auto i = indexOf(a)) {
if (const auto j = indexOf(b)) {
return *j - *i;
}
}
return std::nullopt;
}
std::optional<Id> nearest(Id id) const {
if (const auto it = ranges::lower_bound(_ids, id); it != _ids.end()) {
return *it;
} else if (_ids.empty()) {
return std::nullopt;
}
return _ids.back();
}
void reverse() {
ranges::reverse(_ids);
std::swap(_skippedBefore, _skippedAfter);
}
private:
IdsContainer _ids;
std::optional<int> _fullCount;
std::optional<int> _skippedBefore;
std::optional<int> _skippedAfter;
};

View File

@@ -46,6 +46,10 @@ void MegagroupInfo::setLocation(const ChannelLocation &location) {
_location = location;
}
bool MegagroupInfo::updateBotCommands(const MTPVector<MTPBotInfo> &data) {
return Data::UpdateBotCommands(_botCommands, data);
}
ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id)
: PeerData(owner, id)
, inputChannel(
@@ -803,15 +807,6 @@ void ApplyChannelUpdate(
const auto chat = channel->owner().chat(migratedFrom->v);
Data::ApplyMigration(chat, channel);
}
for (const auto &item : update.vbot_info().v) {
auto &owner = channel->owner();
item.match([&](const MTPDbotInfo &info) {
if (const auto user = owner.userLoaded(info.vuser_id().v)) {
user->setBotInfo(item);
session->api().fullPeerUpdated().notify(user);
}
});
}
channel->setAbout(qs(update.vabout()));
channel->setMembersCount(update.vparticipants_count().value_or_empty());
channel->setAdminsCount(update.vadmins_count().value_or_empty());
@@ -867,6 +862,9 @@ void ApplyChannelUpdate(
SetTopPinnedMessageId(channel, pinned->v);
}
if (channel->isMegagroup()) {
if (channel->mgInfo->updateBotCommands(update.vbot_info())) {
channel->owner().botCommandsChanged(channel);
}
const auto stickerSet = update.vstickerset();
const auto set = stickerSet ? &stickerSet->c_stickerSet() : nullptr;
const auto newSetId = (set ? set->vid().v : 0);

View File

@@ -56,6 +56,12 @@ public:
const ChannelLocation *getLocation() const;
void setLocation(const ChannelLocation &location);
bool updateBotCommands(const MTPVector<MTPBotInfo> &data);
[[nodiscard]] auto botCommands() const
-> const base::flat_map<UserId, std::vector<BotCommand>> & {
return _botCommands;
}
std::deque<not_null<UserData*>> lastParticipants;
base::flat_map<not_null<UserData*>, Admin> lastAdmins;
base::flat_map<not_null<UserData*>, Restricted> lastRestricted;
@@ -82,6 +88,7 @@ public:
private:
ChatData *_migratedFrom = nullptr;
ChannelLocation _location;
base::flat_map<UserId, std::vector<BotCommand>> _botCommands;
};

View File

@@ -246,6 +246,12 @@ PeerId ChatData::groupCallDefaultJoinAs() const {
return _callDefaultJoinAs;
}
void ChatData::setBotCommands(const MTPVector<MTPBotInfo> &data) {
if (Data::UpdateBotCommands(_botCommands, data)) {
owner().botCommandsChanged(this);
}
}
namespace Data {
void ApplyChatUpdate(
@@ -393,15 +399,9 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
chat->setMessagesTTL(update.vttl_period().value_or_empty());
if (const auto info = update.vbot_info()) {
for (const auto &item : info->v) {
item.match([&](const MTPDbotInfo &data) {
const auto userId = data.vuser_id().v;
if (const auto bot = chat->owner().userLoaded(userId)) {
bot->setBotInfo(item);
chat->session().api().fullPeerUpdated().notify(bot);
}
});
}
chat->setBotCommands(*info);
} else {
chat->setBotCommands(MTP_vector<MTPBotInfo>());
}
chat->setFullFlags(update.vflags().v);
if (const auto photo = update.vchat_photo()) {

View File

@@ -173,6 +173,12 @@ public:
void setGroupCallDefaultJoinAs(PeerId peerId);
[[nodiscard]] PeerId groupCallDefaultJoinAs() const;
void setBotCommands(const MTPVector<MTPBotInfo> &data);
[[nodiscard]] auto botCommands() const
-> const base::flat_map<UserId, std::vector<BotCommand>> & {
return _botCommands;
}
// Still public data members.
const MTPint inputChat;
@@ -198,6 +204,7 @@ private:
std::unique_ptr<Data::GroupCall> _call;
PeerId _callDefaultJoinAs = 0;
base::flat_map<UserId, std::vector<BotCommand>> _botCommands;
ChannelData *_migratedTo = nullptr;
rpl::lifetime _lifetime;

View File

@@ -442,12 +442,17 @@ bool DocumentData::checkWallPaperProperties() {
}
void DocumentData::updateThumbnails(
const QByteArray &inlineThumbnailBytes,
const InlineImageLocation &inlineThumbnail,
const ImageWithLocation &thumbnail,
const ImageWithLocation &videoThumbnail) {
if (!inlineThumbnailBytes.isEmpty()
if (!inlineThumbnail.bytes.isEmpty()
&& _inlineThumbnailBytes.isEmpty()) {
_inlineThumbnailBytes = inlineThumbnailBytes;
_inlineThumbnailBytes = inlineThumbnail.bytes;
if (inlineThumbnail.isPath) {
_flags |= Flag::InlineThumbnailIsPath;
} else {
_flags &= ~Flag::InlineThumbnailIsPath;
}
}
Data::UpdateCloudFile(
_thumbnail,

View File

@@ -171,13 +171,16 @@ public:
[[nodiscard]] int videoThumbnailByteSize() const;
void updateThumbnails(
const QByteArray &inlineThumbnailBytes,
const InlineImageLocation &inlineThumbnail,
const ImageWithLocation &thumbnail,
const ImageWithLocation &videoThumbnail);
[[nodiscard]] QByteArray inlineThumbnailBytes() const {
return _inlineThumbnailBytes;
}
[[nodiscard]] bool inlineThumbnailIsPath() const {
return (_flags & Flag::InlineThumbnailIsPath);
}
void clearInlineThumbnailBytes() {
_inlineThumbnailBytes = QByteArray();
}
@@ -257,6 +260,7 @@ private:
DownloadCancelled = 0x10,
LoadedInMediaCache = 0x20,
HasAttachedStickers = 0x40,
InlineThumbnailIsPath = 0x80,
};
using Flags = base::flags<Flag>;
friend constexpr bool is_flag_type(Flag) { return true; };

View File

@@ -172,7 +172,7 @@ void DocumentMedia::setGoodThumbnail(QImage thumbnail) {
}
Image *DocumentMedia::thumbnailInline() const {
if (!_inlineThumbnail) {
if (!_inlineThumbnail && !_owner->inlineThumbnailIsPath()) {
const auto bytes = _owner->inlineThumbnailBytes();
if (!bytes.isEmpty()) {
auto image = Images::FromInlineBytes(bytes);
@@ -186,6 +186,19 @@ Image *DocumentMedia::thumbnailInline() const {
return _inlineThumbnail.get();
}
const QPainterPath &DocumentMedia::thumbnailPath() const {
if (_pathThumbnail.isEmpty() && _owner->inlineThumbnailIsPath()) {
const auto bytes = _owner->inlineThumbnailBytes();
if (!bytes.isEmpty()) {
_pathThumbnail = Images::PathFromInlineBytes(bytes);
if (_pathThumbnail.isEmpty()) {
_owner->clearInlineThumbnailBytes();
}
}
}
return _pathThumbnail;
}
Image *DocumentMedia::thumbnail() const {
return _thumbnail.get();
}

View File

@@ -52,6 +52,7 @@ public:
void setGoodThumbnail(QImage thumbnail);
[[nodiscard]] Image *thumbnailInline() const;
[[nodiscard]] const QPainterPath &thumbnailPath() const;
[[nodiscard]] Image *thumbnail() const;
[[nodiscard]] QSize thumbnailSize() const;
@@ -102,6 +103,7 @@ private:
const not_null<DocumentData*> _owner;
std::unique_ptr<Image> _goodThumbnail;
mutable std::unique_ptr<Image> _inlineThumbnail;
mutable QPainterPath _pathThumbnail;
std::unique_ptr<Image> _thumbnail;
std::unique_ptr<Image> _sticker;
QByteArray _bytes;

View File

@@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/empty_userpic.h"
#include "ui/text/text_options.h"
#include "ui/toasts/common_toasts.h"
#include "ui/ui_utility.h"
#include "history/history.h"
#include "history/view/history_view_element.h"
#include "history/history_item.h"
@@ -41,7 +42,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_facade.h"
#include "storage/storage_shared_media.h"
#include "facades.h" // Ui::showPeerProfile
#include "app.h"
namespace {
@@ -84,6 +84,75 @@ PeerId FakePeerIdForJustName(const QString &name) {
: base::crc32(name.constData(), name.size() * sizeof(QChar)));
}
bool UpdateBotCommands(
std::vector<BotCommand> &commands,
const MTPVector<MTPBotCommand> &data) {
const auto &v = data.v;
commands.reserve(v.size());
auto result = false;
auto index = 0;
for (const auto &command : v) {
command.match([&](const MTPDbotCommand &data) {
const auto command = qs(data.vcommand());
const auto description = qs(data.vdescription());
if (commands.size() <= index) {
commands.push_back({
.command = command,
.description = description,
});
result = true;
} else {
auto &entry = commands[index];
if (entry.command != command
|| entry.description != description) {
entry.command = command;
entry.description = description;
result = true;
}
}
++index;
});
}
if (index < commands.size()) {
result = true;
}
commands.resize(index);
return result;
}
bool UpdateBotCommands(
base::flat_map<UserId, std::vector<BotCommand>> &commands,
const MTPVector<MTPBotInfo> &data) {
auto result = false;
auto filled = base::flat_set<UserId>();
filled.reserve(data.v.size());
for (const auto &item : data.v) {
item.match([&](const MTPDbotInfo &data) {
const auto id = UserId(data.vuser_id().v);
if (!filled.emplace(id).second) {
LOG(("API Error: Two BotInfo for a single bot."));
return;
}
if (data.vcommands().v.isEmpty()) {
if (commands.remove(id)) {
result = true;
}
} else if (UpdateBotCommands(commands[id], data.vcommands())) {
result = true;
}
});
}
for (auto i = begin(commands); i != end(commands);) {
if (filled.contains(i->first)) {
++i;
} else {
i = commands.erase(i);
result = true;
}
}
return result;
}
} // namespace Data
PeerClickHandler::PeerClickHandler(not_null<PeerData*> peer)
@@ -348,7 +417,7 @@ QPixmap PeerData::genUserpic(
Painter p(&result);
paintUserpic(p, view, 0, 0, size);
}
return App::pixmapFromImageInPlace(std::move(result));
return Ui::PixmapFromImage(std::move(result));
}
QPixmap PeerData::genUserpicRounded(
@@ -364,7 +433,7 @@ QPixmap PeerData::genUserpicRounded(
Painter p(&result);
paintUserpicRounded(p, view, 0, 0, size);
}
return App::pixmapFromImageInPlace(std::move(result));
return Ui::PixmapFromImage(std::move(result));
}
Data::FileOrigin PeerData::userpicOrigin() const {

View File

@@ -22,6 +22,11 @@ using ChatRestriction = MTPDchatBannedRights::Flag;
using ChatAdminRights = MTPDchatAdminRights::Flags;
using ChatRestrictions = MTPDchatBannedRights::Flags;
struct BotCommand {
QString command;
QString description;
};
namespace Ui {
class EmptyUserpic;
} // namespace Ui
@@ -100,6 +105,13 @@ struct UnavailableReason {
[[nodiscard]] TimeId ChatBannedRightsUntilDate(
const MTPChatBannedRights &rights);
bool UpdateBotCommands(
std::vector<BotCommand> &commands,
const MTPVector<MTPBotCommand> &data);
bool UpdateBotCommands(
base::flat_map<UserId, std::vector<BotCommand>> &commands,
const MTPVector<MTPBotInfo> &data);
} // namespace Data
class PeerClickHandler : public ClickHandler {

View File

@@ -134,18 +134,25 @@ std::vector<UnavailableReason> ExtractUnavailableReasons(
}) | ranges::to_vector;
}
[[nodiscard]] QByteArray FindInlineThumbnail(
[[nodiscard]] InlineImageLocation FindInlineThumbnail(
const QVector<MTPPhotoSize> &sizes) {
const auto i = ranges::find(
sizes,
mtpc_photoStrippedSize,
&MTPPhotoSize::type);
const auto j = ranges::find(
sizes,
mtpc_photoPathSize,
&MTPPhotoSize::type);
return (i != sizes.end())
? i->c_photoStrippedSize().vbytes().v
: QByteArray();
? InlineImageLocation{ i->c_photoStrippedSize().vbytes().v, false }
: (j != sizes.end())
? InlineImageLocation{ j->c_photoPathSize().vbytes().v, true }
: InlineImageLocation();
}
[[nodiscard]] QByteArray FindDocumentInlineThumbnail(const MTPDdocument &data) {
[[nodiscard]] InlineImageLocation FindDocumentInlineThumbnail(
const MTPDdocument &data) {
return FindInlineThumbnail(data.vthumbs().value_or_empty());
}
@@ -193,7 +200,8 @@ std::vector<UnavailableReason> ExtractUnavailableReasons(
}
[[nodiscard]] QByteArray FindPhotoInlineThumbnail(const MTPDphoto &data) {
return FindInlineThumbnail(data.vsizes().v);
const auto thumbnail = FindInlineThumbnail(data.vsizes().v);
return !thumbnail.isPath ? thumbnail.bytes : QByteArray();
}
[[nodiscard]] int VideoStartTime(const MTPDvideoSize &data) {
@@ -1080,11 +1088,11 @@ rpl::producer<not_null<UserData*>> Session::userIsBotChanges() const {
return _userIsBotChanges.events();
}
void Session::botCommandsChanged(not_null<UserData*> user) {
_botCommandsChanges.fire_copy(user);
void Session::botCommandsChanged(not_null<PeerData*> peer) {
_botCommandsChanges.fire_copy(peer);
}
rpl::producer<not_null<UserData*>> Session::botCommandsChanges() const {
rpl::producer<not_null<PeerData*>> Session::botCommandsChanges() const {
return _botCommandsChanges.events();
}
@@ -2656,7 +2664,7 @@ not_null<DocumentData*> Session::processDocument(
data.vdate().v,
data.vattributes().v,
qs(data.vmime_type()),
QByteArray(),
InlineImageLocation(),
thumbnail,
ImageWithLocation(),
data.vdc_id().v,
@@ -2673,7 +2681,7 @@ not_null<DocumentData*> Session::document(
TimeId date,
const QVector<MTPDocumentAttribute> &attributes,
const QString &mime,
const QByteArray &inlineThumbnailBytes,
const InlineImageLocation &inlineThumbnail,
const ImageWithLocation &thumbnail,
const ImageWithLocation &videoThumbnail,
int32 dc,
@@ -2686,7 +2694,7 @@ not_null<DocumentData*> Session::document(
date,
attributes,
mime,
inlineThumbnailBytes,
inlineThumbnail,
thumbnail,
videoThumbnail,
dc,
@@ -2754,7 +2762,7 @@ DocumentData *Session::documentFromWeb(
base::unixtime::now(),
data.vattributes().v,
data.vmime_type().v,
QByteArray(),
InlineImageLocation(),
ImageWithLocation{ .location = thumbnailLocation },
ImageWithLocation{ .location = videoThumbnailLocation },
session().mainDcId(),
@@ -2776,7 +2784,7 @@ DocumentData *Session::documentFromWeb(
base::unixtime::now(),
data.vattributes().v,
data.vmime_type().v,
QByteArray(),
InlineImageLocation(),
ImageWithLocation{ .location = thumbnailLocation },
ImageWithLocation{ .location = videoThumbnailLocation },
session().mainDcId(),
@@ -2796,7 +2804,7 @@ void Session::documentApplyFields(
void Session::documentApplyFields(
not_null<DocumentData*> document,
const MTPDdocument &data) {
const auto inlineThumbnailBytes = FindDocumentInlineThumbnail(data);
const auto inlineThumbnail = FindDocumentInlineThumbnail(data);
const auto thumbnailSize = FindDocumentThumbnail(data);
const auto videoThumbnailSize = FindDocumentVideoThumbnail(data);
const auto prepared = Images::FromPhotoSize(
@@ -2813,7 +2821,7 @@ void Session::documentApplyFields(
data.vdate().v,
data.vattributes().v,
qs(data.vmime_type()),
inlineThumbnailBytes,
inlineThumbnail,
prepared,
videoThumbnail,
data.vdc_id().v,
@@ -2827,7 +2835,7 @@ void Session::documentApplyFields(
TimeId date,
const QVector<MTPDocumentAttribute> &attributes,
const QString &mime,
const QByteArray &inlineThumbnailBytes,
const InlineImageLocation &inlineThumbnail,
const ImageWithLocation &thumbnail,
const ImageWithLocation &videoThumbnail,
int32 dc,
@@ -2838,7 +2846,7 @@ void Session::documentApplyFields(
document->date = date;
document->setMimeString(mime);
document->updateThumbnails(
inlineThumbnailBytes,
inlineThumbnail,
thumbnail,
videoThumbnail);
document->size = size;

View File

@@ -215,8 +215,8 @@ public:
void userIsBotChanged(not_null<UserData*> user);
[[nodiscard]] rpl::producer<not_null<UserData*>> userIsBotChanges() const;
void botCommandsChanged(not_null<UserData*> user);
[[nodiscard]] rpl::producer<not_null<UserData*>> botCommandsChanges() const;
void botCommandsChanged(not_null<PeerData*> peer);
[[nodiscard]] rpl::producer<not_null<PeerData*>> botCommandsChanges() const;
struct ItemVisibilityQuery {
not_null<HistoryItem*> item;
@@ -476,7 +476,7 @@ public:
TimeId date,
const QVector<MTPDocumentAttribute> &attributes,
const QString &mime,
const QByteArray &inlineThumbnailBytes,
const InlineImageLocation &inlineThumbnail,
const ImageWithLocation &thumbnail,
const ImageWithLocation &videoThumbnail,
int32 dc,
@@ -743,7 +743,7 @@ private:
TimeId date,
const QVector<MTPDocumentAttribute> &attributes,
const QString &mime,
const QByteArray &inlineThumbnailBytes,
const InlineImageLocation &inlineThumbnail,
const ImageWithLocation &thumbnail,
const ImageWithLocation &videoThumbnail,
int32 dc,
@@ -831,7 +831,7 @@ private:
rpl::event_stream<Data::Folder*> _chatsListLoadedEvents;
rpl::event_stream<Data::Folder*> _chatsListChanged;
rpl::event_stream<not_null<UserData*>> _userIsBotChanges;
rpl::event_stream<not_null<UserData*>> _botCommandsChanges;
rpl::event_stream<not_null<PeerData*>> _botCommandsChanges;
base::Observable<ItemVisibilityQuery> _queryItemVisibility;
rpl::event_stream<IdChange> _itemIdChanges;
rpl::event_stream<not_null<const HistoryItem*>> _itemLayoutChanges;

View File

@@ -16,8 +16,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_shared_media.h"
#include "history/history.h"
#include "history/history_item.h"
#include "data/data_document.h"
#include "data/data_media_types.h"
#include "data/data_photo.h"
#include "data/data_scheduled_messages.h"
#include "data/data_sparse_ids.h"
#include "data/data_session.h"
#include "info/info_memento.h"
@@ -30,6 +32,51 @@ namespace {
using Type = Storage::SharedMediaType;
bool IsItemGoodForType(const not_null<HistoryItem*> item, Type type) {
const auto media = item->media();
if (!media || media->webpage()) {
return false;
}
const auto photo = media->photo();
const auto photoType = (type == Type::Photo);
const auto photoVideoType = (type == Type::PhotoVideo);
if ((photoType || photoVideoType) && photo) {
return true;
}
const auto document = media->document();
if (!document) {
return false;
}
const auto voiceType = (type == Type::VoiceFile);
const auto voiceDoc = document->isVoiceMessage();
const auto roundType = (type == Type::RoundFile);
const auto roundDoc = document->isVideoMessage();
const auto audioType = (type == Type::MusicFile);
const auto audioDoc = document->isAudioFile();
const auto gifType = (type == Type::GIF);
const auto gifDoc = document->isGifv();
const auto videoType = (type == Type::Video);
const auto videoDoc = document->isVideoFile();
const auto voiceRoundType = (type == Type::RoundVoiceFile);
const auto fileType = (type == Type::File);
return (audioType && audioDoc)
|| (voiceType && voiceDoc)
|| (roundType && roundDoc)
|| (voiceRoundType && (roundDoc || voiceDoc))
|| (gifType && gifDoc)
|| ((videoType || photoVideoType) && videoDoc)
|| (fileType && (document->isTheme()
|| document->isImage()
|| !document->canBeStreamed()));
}
} // namespace
std::optional<Storage::SharedMediaType> SharedMediaOverviewType(
@@ -153,6 +200,55 @@ rpl::producer<SparseIdsSlice> SharedMediaViewer(
};
}
rpl::producer<SparseIdsMergedSlice> SharedScheduledMediaViewer(
not_null<Main::Session*> session,
SharedMediaMergedKey key,
int limitBefore,
int limitAfter) {
Expects(!IsServerMsgId(key.mergedKey.universalId));
Expects((key.mergedKey.universalId != 0)
|| (limitBefore == 0 && limitAfter == 0));
const auto history = session->data().history(key.mergedKey.peerId);
return rpl::single(
rpl::empty_value()
) | rpl::then(
session->data().scheduledMessages().updates(history)
) | rpl::map([=] {
const auto list = session->data().scheduledMessages().list(history);
auto items = ranges::views::all(
list.ids
) | ranges::views::transform([=](const FullMsgId &fullId) {
return session->data().message(fullId);
}) | ranges::views::filter([=](HistoryItem *item) {
return item
? IsItemGoodForType(item, key.type)
: false;
}) | ranges::to_vector;
ranges::sort(items, ranges::less(), &HistoryItem::position);
auto finishMsgIds = ranges::views::all(
items
) | ranges::views::transform([=](not_null<HistoryItem*> item) {
return item->fullId().msg;
}) | ranges::to_vector;
const auto fullCount = finishMsgIds.size();
auto unsorted = SparseUnsortedIdsSlice(
std::move(finishMsgIds),
fullCount,
list.skippedBefore,
list.skippedAfter);
return SparseIdsMergedSlice(
key.mergedKey,
std::move(unsorted));
});
}
rpl::producer<SparseIdsMergedSlice> SharedMediaMergedViewer(
not_null<Main::Session*> session,
SharedMediaMergedKey key,
@@ -374,12 +470,29 @@ rpl::producer<SharedMediaWithLastSlice> SharedMediaWithLastViewer(
int limitBefore,
int limitAfter) {
return [=](auto consumer) {
auto viewerKey = SharedMediaMergedKey(
SharedMediaWithLastSlice::ViewerKey(key),
key.type);
if (std::get_if<not_null<PhotoData*>>(&key.universalId)) {
return SharedMediaMergedViewer(
session,
SharedMediaMergedKey(
SharedMediaWithLastSlice::ViewerKey(key),
key.type),
std::move(viewerKey),
limitBefore,
limitAfter
) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) {
consumer.put_next(SharedMediaWithLastSlice(
session,
key,
std::move(update),
std::nullopt));
});
}
if (key.scheduled) {
return SharedScheduledMediaViewer(
session,
std::move(viewerKey),
limitBefore,
limitAfter
) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) {
@@ -393,9 +506,7 @@ rpl::producer<SharedMediaWithLastSlice> SharedMediaWithLastViewer(
return rpl::combine(
SharedMediaMergedViewer(
session,
SharedMediaMergedKey(
SharedMediaWithLastSlice::ViewerKey(key),
key.type),
std::move(viewerKey),
limitBefore,
limitAfter),
SharedMediaMergedViewer(

View File

@@ -50,6 +50,12 @@ struct SharedMediaMergedKey {
};
rpl::producer<SparseIdsMergedSlice> SharedScheduledMediaViewer(
not_null<Main::Session*> session,
SharedMediaMergedKey key,
int limitBefore,
int limitAfter);
rpl::producer<SparseIdsMergedSlice> SharedMediaMergedViewer(
not_null<Main::Session*> session,
SharedMediaMergedKey key,
@@ -71,11 +77,13 @@ public:
PeerId peerId,
PeerId migratedPeerId,
Type type,
UniversalMsgId universalId)
UniversalMsgId universalId,
bool scheduled = false)
: peerId(peerId)
, migratedPeerId(migratedPeerId)
, type(type)
, universalId(universalId) {
, universalId(universalId)
, scheduled(scheduled) {
Expects(v::is<MessageId>(universalId) || type == Type::ChatPhoto);
}
@@ -93,6 +101,7 @@ public:
PeerId migratedPeerId = 0;
Type type = Type::kCount;
UniversalMsgId universalId;
bool scheduled = false;
};

View File

@@ -10,53 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/combine.h>
#include "storage/storage_sparse_ids_list.h"
SparseIdsSlice::SparseIdsSlice(
const base::flat_set<MsgId> &ids,
MsgRange range,
std::optional<int> fullCount,
std::optional<int> skippedBefore,
std::optional<int> skippedAfter)
: _ids(ids)
, _range(range)
, _fullCount(fullCount)
, _skippedBefore(skippedBefore)
, _skippedAfter(skippedAfter) {
}
std::optional<int> SparseIdsSlice::indexOf(MsgId msgId) const {
auto it = _ids.find(msgId);
if (it != _ids.end()) {
return (it - _ids.begin());
}
return std::nullopt;
}
MsgId SparseIdsSlice::operator[](int index) const {
Expects(index >= 0 && index < size());
return *(_ids.begin() + index);
}
std::optional<int> SparseIdsSlice::distance(
MsgId a,
MsgId b) const {
if (auto i = indexOf(a)) {
if (auto j = indexOf(b)) {
return *j - *i;
}
}
return std::nullopt;
}
std::optional<MsgId> SparseIdsSlice::nearest(MsgId msgId) const {
if (auto it = ranges::lower_bound(_ids, msgId); it != _ids.end()) {
return *it;
} else if (_ids.empty()) {
return std::nullopt;
}
return _ids.back();
}
SparseIdsMergedSlice::SparseIdsMergedSlice(Key key)
: SparseIdsMergedSlice(
key,
@@ -73,33 +26,48 @@ SparseIdsMergedSlice::SparseIdsMergedSlice(
, _migrated(std::move(migrated)) {
}
SparseIdsMergedSlice::SparseIdsMergedSlice(
Key key,
SparseUnsortedIdsSlice scheduled)
: _key(key)
, _scheduled(std::move(scheduled)) {
}
std::optional<int> SparseIdsMergedSlice::fullCount() const {
return Add(
_part.fullCount(),
_migrated ? _migrated->fullCount() : 0);
return _scheduled
? _scheduled->fullCount()
: Add(
_part.fullCount(),
_migrated ? _migrated->fullCount() : 0);
}
std::optional<int> SparseIdsMergedSlice::skippedBefore() const {
return Add(
isolatedInMigrated() ? 0 : _part.skippedBefore(),
_migrated
? (isolatedInPart()
? _migrated->fullCount()
: _migrated->skippedBefore())
: 0
);
return _scheduled
? _scheduled->skippedBefore()
: Add(
isolatedInMigrated() ? 0 : _part.skippedBefore(),
_migrated
? (isolatedInPart()
? _migrated->fullCount()
: _migrated->skippedBefore())
: 0
);
}
std::optional<int> SparseIdsMergedSlice::skippedAfter() const {
return Add(
isolatedInMigrated() ? _part.fullCount() : _part.skippedAfter(),
isolatedInPart() ? 0 : _migrated->skippedAfter()
);
return _scheduled
? _scheduled->skippedAfter()
: Add(
isolatedInMigrated() ? _part.fullCount() : _part.skippedAfter(),
isolatedInPart() ? 0 : _migrated->skippedAfter()
);
}
std::optional<int> SparseIdsMergedSlice::indexOf(
FullMsgId fullId) const {
return isFromPart(fullId)
return _scheduled
? _scheduled->indexOf(fullId.msg)
: isFromPart(fullId)
? (_part.indexOf(fullId.msg) | func::add(migratedSize()))
: isolatedInPart()
? std::nullopt
@@ -109,14 +77,20 @@ std::optional<int> SparseIdsMergedSlice::indexOf(
}
int SparseIdsMergedSlice::size() const {
return (isolatedInPart() ? 0 : migratedSize())
+ (isolatedInMigrated() ? 0 : _part.size());
return _scheduled
? _scheduled->size()
: (isolatedInPart() ? 0 : migratedSize())
+ (isolatedInMigrated() ? 0 : _part.size());
}
FullMsgId SparseIdsMergedSlice::operator[](int index) const {
Expects(index >= 0 && index < size());
if (auto size = migratedSize()) {
if (_scheduled) {
return ComputeId(_key.peerId, (*_scheduled)[index]);
}
if (const auto size = migratedSize()) {
if (index < size) {
return ComputeId(_key.migratedPeerId, (*_migrated)[index]);
}
@@ -128,8 +102,8 @@ FullMsgId SparseIdsMergedSlice::operator[](int index) const {
std::optional<int> SparseIdsMergedSlice::distance(
const Key &a,
const Key &b) const {
if (auto i = indexOf(ComputeId(a))) {
if (auto j = indexOf(ComputeId(b))) {
if (const auto i = indexOf(ComputeId(a))) {
if (const auto j = indexOf(ComputeId(b))) {
return *j - *i;
}
}
@@ -138,10 +112,15 @@ std::optional<int> SparseIdsMergedSlice::distance(
auto SparseIdsMergedSlice::nearest(
UniversalMsgId id) const -> std::optional<FullMsgId> {
auto convertFromPartNearest = [&](MsgId result) {
if (_scheduled) {
if (const auto nearestId = _scheduled->nearest(id)) {
return ComputeId(_key.peerId, *nearestId);
}
}
const auto convertFromPartNearest = [&](MsgId result) {
return ComputeId(_key.peerId, result);
};
auto convertFromMigratedNearest = [&](MsgId result) {
const auto convertFromMigratedNearest = [&](MsgId result) {
return ComputeId(_key.migratedPeerId, result);
};
if (IsServerMsgId(id)) {
@@ -245,7 +224,6 @@ bool SparseIdsSliceBuilder::removeOne(MsgId messageId) {
bool SparseIdsSliceBuilder::removeAll() {
_ids = {};
_range = { 0, ServerMaxMsgId };
_fullCount = 0;
_skippedBefore = 0;
_skippedAfter = 0;
@@ -254,9 +232,6 @@ bool SparseIdsSliceBuilder::removeAll() {
bool SparseIdsSliceBuilder::invalidateBottom() {
_fullCount = _skippedAfter = std::nullopt;
if (_range.till == ServerMaxMsgId) {
_range.till = _ids.empty() ? _range.from : _ids.back();
}
checkInsufficient();
return true;
}
@@ -389,7 +364,6 @@ void SparseIdsSliceBuilder::requestMessagesCount() {
SparseIdsSlice SparseIdsSliceBuilder::snapshot() const {
return SparseIdsSlice(
_ids,
_range,
_fullCount,
_skippedBefore,
_skippedAfter);
@@ -440,4 +414,4 @@ rpl::producer<SparseIdsMergedSlice> SparseIdsMergedSlice::CreateViewer(
std::move(migrated)));
});
};
}
}

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_abstract_sparse_ids.h"
#include "data/data_messages.h"
namespace Storage {
@@ -14,36 +15,15 @@ struct SparseIdsListResult;
struct SparseIdsSliceUpdate;
} // namespace Storage
class SparseIdsSlice {
class SparseIdsSlice final : public AbstractSparseIds<base::flat_set<MsgId>> {
public:
using Key = MsgId;
SparseIdsSlice() = default;
SparseIdsSlice(
const base::flat_set<MsgId> &ids,
MsgRange range,
std::optional<int> fullCount,
std::optional<int> skippedBefore,
std::optional<int> skippedAfter);
std::optional<int> fullCount() const { return _fullCount; }
std::optional<int> skippedBefore() const { return _skippedBefore; }
std::optional<int> skippedAfter() const { return _skippedAfter; }
std::optional<int> indexOf(MsgId msgId) const;
int size() const { return _ids.size(); }
MsgId operator[](int index) const;
std::optional<int> distance(MsgId a, MsgId b) const;
std::optional<MsgId> nearest(MsgId msgId) const;
private:
base::flat_set<MsgId> _ids;
MsgRange _range;
std::optional<int> _fullCount;
std::optional<int> _skippedBefore;
std::optional<int> _skippedAfter;
using AbstractSparseIds<base::flat_set<MsgId>>::AbstractSparseIds;
};
using SparseUnsortedIdsSlice = AbstractSparseIds<std::vector<MsgId>>;
class SparseIdsMergedSlice {
public:
using UniversalMsgId = MsgId;
@@ -51,9 +31,11 @@ public:
Key(
PeerId peerId,
PeerId migratedPeerId,
UniversalMsgId universalId)
UniversalMsgId universalId,
bool scheduled = false)
: peerId(peerId)
, migratedPeerId(migratedPeerId)
, scheduled(scheduled)
, migratedPeerId(scheduled ? 0 : migratedPeerId)
, universalId(universalId) {
}
@@ -67,6 +49,7 @@ public:
}
PeerId peerId = 0;
bool scheduled = false;
PeerId migratedPeerId = 0;
UniversalMsgId universalId = 0;
@@ -77,6 +60,9 @@ public:
Key key,
SparseIdsSlice part,
std::optional<SparseIdsSlice> migrated);
SparseIdsMergedSlice(
Key key,
SparseUnsortedIdsSlice scheduled);
std::optional<int> fullCount() const;
std::optional<int> skippedBefore() const;
@@ -155,6 +141,7 @@ private:
Key _key;
SparseIdsSlice _part;
std::optional<SparseIdsSlice> _migrated;
std::optional<SparseUnsortedIdsSlice> _scheduled;
};
@@ -205,7 +192,6 @@ private:
Key _key;
base::flat_set<MsgId> _ids;
MsgRange _range;
std::optional<int> _fullCount;
std::optional<int> _skippedBefore;
std::optional<int> _skippedAfter;

View File

@@ -95,7 +95,7 @@ class PeerData;
class UserData;
class ChatData;
class ChannelData;
class BotCommand;
struct BotCommand;
struct BotInfo;
namespace Data {

View File

@@ -24,32 +24,6 @@ using UpdateFlag = Data::PeerUpdate::Flag;
} // namespace
BotCommand::BotCommand(
const QString &command,
const QString &description)
: command(command)
, _description(description) {
}
bool BotCommand::setDescription(const QString &description) {
if (_description != description) {
_description = description;
_descriptionText = Ui::Text::String();
return true;
}
return false;
}
const Ui::Text::String &BotCommand::descriptionText() const {
if (_descriptionText.isEmpty() && !_description.isEmpty()) {
_descriptionText.setText(
st::defaultTextStyle,
_description,
Ui::NameTextOptions());
}
return _descriptionText;
}
UserData::UserData(not_null<Data::Session*> owner, PeerId id)
: PeerData(owner, id) {
}
@@ -132,7 +106,7 @@ void UserData::setBotInfoVersion(int version) {
botInfo->version = version;
owner().userIsBotChanged(this);
} else if (botInfo->version < version) {
if (!botInfo->commands.isEmpty()) {
if (!botInfo->commands.empty()) {
botInfo->commands.clear();
owner().botCommandsChanged(this);
}
@@ -155,34 +129,9 @@ void UserData::setBotInfo(const MTPBotInfo &info) {
botInfo->description = desc;
botInfo->text = Ui::Text::String(st::msgMinWidth);
}
auto &v = d.vcommands().v;
botInfo->commands.reserve(v.size());
auto changedCommands = false;
int32 j = 0;
for (const auto &command : v) {
command.match([&](const MTPDbotCommand &data) {
const auto cmd = qs(data.vcommand());
const auto desc = qs(data.vdescription());
if (botInfo->commands.size() <= j) {
botInfo->commands.push_back(BotCommand(cmd, desc));
changedCommands = true;
} else {
if (botInfo->commands[j].command != cmd) {
botInfo->commands[j].command = cmd;
changedCommands = true;
}
if (botInfo->commands[j].setDescription(desc)) {
changedCommands = true;
}
}
++j;
});
}
while (j < botInfo->commands.size()) {
botInfo->commands.pop_back();
changedCommands = true;
}
const auto changedCommands = Data::UpdateBotCommands(
botInfo->commands,
d.vcommands());
botInfo->inited = true;

View File

@@ -10,28 +10,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer.h"
#include "dialogs/dialogs_key.h"
class BotCommand {
public:
BotCommand(const QString &command, const QString &description);
bool setDescription(const QString &description);
const Ui::Text::String &descriptionText() const;
QString command;
private:
QString _description;
mutable Ui::Text::String _descriptionText;
};
struct BotInfo {
bool inited = false;
bool readsAllHistory = false;
bool cantJoinGroups = false;
int version = 0;
QString description, inlinePlaceholder;
QList<BotCommand> commands;
std::vector<BotCommand> commands;
Ui::Text::String text = { int(st::msgMinWidth) }; // description
QString startToken, startGroupToken, shareGameShortName;

View File

@@ -64,39 +64,23 @@ UserPhotosSlice::UserPhotosSlice(
std::optional<int> fullCount,
std::optional<int> skippedBefore,
std::optional<int> skippedAfter)
: _key(key)
, _ids(std::move(ids))
, _fullCount(fullCount)
, _skippedBefore(skippedBefore)
, _skippedAfter(skippedAfter) {
: AbstractSparseIds<std::deque<PhotoId>>(
ids,
fullCount,
skippedBefore,
skippedAfter)
, _key(key) {
}
void UserPhotosSlice::reverse() {
ranges::reverse(_ids);
std::swap(_skippedBefore, _skippedAfter);
}
std::optional<int> UserPhotosSlice::indexOf(PhotoId photoId) const {
auto it = ranges::find(_ids, photoId);
if (it != _ids.end()) {
return (it - _ids.begin());
}
return std::nullopt;
}
PhotoId UserPhotosSlice::operator[](int index) const {
Expects(index >= 0 && index < size());
return *(_ids.begin() + index);
}
std::optional<int> UserPhotosSlice::distance(const Key &a, const Key &b) const {
std::optional<int> UserPhotosSlice::distance(
const Key &a,
const Key &b) const {
if (a.userId != _key.userId
|| b.userId != _key.userId) {
return std::nullopt;
}
if (auto i = indexOf(a.photoId)) {
if (auto j = indexOf(b.photoId)) {
if (const auto i = indexOf(a.photoId)) {
if (const auto j = indexOf(b.photoId)) {
return *j - *i;
}
}
@@ -248,4 +232,4 @@ rpl::producer<UserPhotosSlice> UserPhotosReversedViewer(
slice.reverse();
return std::move(slice);
});
}
}

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_abstract_sparse_ids.h"
#include "storage/storage_user_photos.h"
#include "base/weak_ptr.h"
@@ -14,7 +15,7 @@ namespace Main {
class Session;
} // namespace Main
class UserPhotosSlice {
class UserPhotosSlice final : public AbstractSparseIds<std::deque<PhotoId>> {
public:
using Key = Storage::UserPhotosKey;
@@ -26,24 +27,11 @@ public:
std::optional<int> skippedBefore,
std::optional<int> skippedAfter);
void reverse();
const Key &key() const { return _key; }
std::optional<int> fullCount() const { return _fullCount; }
std::optional<int> skippedBefore() const { return _skippedBefore; }
std::optional<int> skippedAfter() const { return _skippedAfter; }
std::optional<int> indexOf(PhotoId msgId) const;
int size() const { return _ids.size(); }
PhotoId operator[](int index) const;
std::optional<int> distance(const Key &a, const Key &b) const;
const Key &key() const { return _key; }
private:
Key _key;
std::deque<PhotoId> _ids;
std::optional<int> _fullCount;
std::optional<int> _skippedBefore;
std::optional<int> _skippedAfter;
friend class UserPhotosSliceBuilder;

View File

@@ -86,11 +86,11 @@ rpl::producer<> Stickers::updated() const {
return _updated.events();
}
void Stickers::notifyRecentUpdated() {
_recentUpdated.fire({});
void Stickers::notifyRecentUpdated(Recent recent) {
_recentUpdated.fire(std::move(recent));
}
rpl::producer<> Stickers::recentUpdated() const {
rpl::producer<Stickers::Recent> Stickers::recentUpdated() const {
return _recentUpdated.events();
}
@@ -102,6 +102,14 @@ rpl::producer<> Stickers::savedGifsUpdated() const {
return _savedGifsUpdated.events();
}
void Stickers::notifyStickerSetInstalled(uint64 setId) {
_stickerSetInstalled.fire(std::move(setId));
}
rpl::producer<uint64> Stickers::stickerSetInstalled() const {
return _stickerSetInstalled.events();
}
void Stickers::incrementSticker(not_null<DocumentData*> document) {
if (!document->sticker()
|| document->sticker()->set.type() == mtpc_inputStickerSetEmpty) {
@@ -254,10 +262,12 @@ void Stickers::checkSavedGif(not_null<HistoryItem*> item) {
void Stickers::applyArchivedResult(
const MTPDmessages_stickerSetInstallResultArchive &d) {
auto &v = d.vsets().v;
auto &order = setsOrderRef();
StickersSetsOrder archived;
archived.reserve(v.size());
QMap<uint64, uint64> setsToRequest;
auto masksCount = 0;
auto stickersCount = 0;
for (const auto &stickerSet : v) {
const MTPDstickerSet *setData = nullptr;
switch (stickerSet.type()) {
@@ -279,7 +289,10 @@ void Stickers::applyArchivedResult(
if (set->stickers.isEmpty()) {
setsToRequest.insert(set->id, set->access);
}
auto index = order.indexOf(set->id);
const auto masks = !!(set->flags & MTPDstickerSet::Flag::f_masks);
(masks ? masksCount : stickersCount)++;
auto &order = masks ? maskSetsOrderRef() : setsOrderRef();
const auto index = order.indexOf(set->id);
if (index >= 0) {
order.removeAt(index);
}
@@ -292,8 +305,14 @@ void Stickers::applyArchivedResult(
}
session().api().requestStickerSets();
}
session().local().writeInstalledStickers();
session().local().writeArchivedStickers();
if (stickersCount) {
session().local().writeInstalledStickers();
session().local().writeArchivedStickers();
}
if (masksCount) {
session().local().writeInstalledMasks();
session().local().writeArchivedMasks();
}
Ui::Toast::Show(Ui::Toast::Config{
.text = { tr::lng_stickers_packs_archived(tr::now) },
@@ -351,12 +370,14 @@ void Stickers::installLocally(uint64 setId) {
const auto set = it->second.get();
auto flags = set->flags;
set->flags &= ~(MTPDstickerSet::Flag::f_archived | MTPDstickerSet_ClientFlag::f_unread);
set->flags &= ~(MTPDstickerSet::Flag::f_archived
| MTPDstickerSet_ClientFlag::f_unread);
set->flags |= MTPDstickerSet::Flag::f_installed_date;
set->installDate = base::unixtime::now();
auto changedFlags = flags ^ set->flags;
auto &order = setsOrderRef();
const auto masks = !!(flags & MTPDstickerSet::Flag::f_masks);
auto &order = masks ? maskSetsOrderRef() : setsOrderRef();
int insertAtIndex = 0, currentIndex = order.indexOf(setId);
if (currentIndex != insertAtIndex) {
if (currentIndex > 0) {
@@ -381,10 +402,17 @@ void Stickers::installLocally(uint64 setId) {
session().local().writeFeaturedStickers();
}
if (changedFlags & MTPDstickerSet::Flag::f_archived) {
auto index = archivedSetsOrderRef().indexOf(setId);
auto &archivedOrder = masks
? archivedMaskSetsOrderRef()
: archivedSetsOrderRef();
const auto index = archivedOrder.indexOf(setId);
if (index >= 0) {
archivedSetsOrderRef().removeAt(index);
session().local().writeArchivedStickers();
archivedOrder.removeAt(index);
if (masks) {
session().local().writeArchivedMasks();
} else {
session().local().writeArchivedStickers();
}
}
}
notifyUpdated();
@@ -512,7 +540,7 @@ void Stickers::setIsFaved(
}
session().local().writeFavedStickers();
notifyUpdated();
session().api().stickerSetInstalled(FavedSetId);
notifyStickerSetInstalled(FavedSetId);
}
void Stickers::requestSetToPushFaved(not_null<DocumentData*> document) {
@@ -573,26 +601,41 @@ void Stickers::setFaved(not_null<DocumentData*> document, bool faved) {
}
void Stickers::setsReceived(const QVector<MTPStickerSet> &data, int32 hash) {
auto &setsOrder = setsOrderRef();
const auto masksReceived = ranges::all_of(
data,
[](const MTPStickerSet &set) {
return set.c_stickerSet().is_masks();
});
auto &setsOrder = masksReceived
? maskSetsOrderRef()
: setsOrderRef();
setsOrder.clear();
using Flag = MTPDstickerSet::Flag;
using ClientFlag = MTPDstickerSet_ClientFlag;
auto &sets = setsRef();
QMap<uint64, uint64> setsToRequest;
for (auto &[id, set] : sets) {
if (!(set->flags & MTPDstickerSet::Flag::f_archived)) {
const auto archived = !!(set->flags & Flag::f_archived);
const auto masks = !!(set->flags & MTPDstickerSet::Flag::f_masks);
if (!archived && (masksReceived == masks)) {
// Mark for removing.
set->flags &= ~MTPDstickerSet::Flag::f_installed_date;
set->flags &= ~Flag::f_installed_date;
set->installDate = 0;
}
}
for (const auto &setData : data) {
if (setData.type() == mtpc_stickerSet) {
auto set = feedSet(setData.c_stickerSet());
if (!(set->flags & MTPDstickerSet::Flag::f_archived) || (set->flags & MTPDstickerSet::Flag::f_official)) {
setsOrder.push_back(set->id);
if (set->stickers.isEmpty() || (set->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) {
setsToRequest.insert(set->id, set->access);
}
if (setData.type() != mtpc_stickerSet) {
continue;
}
const auto set = feedSet(setData.c_stickerSet());
if (!(set->flags & Flag::f_archived)
|| (set->flags & Flag::f_official)) {
setsOrder.push_back(set->id);
if (set->stickers.isEmpty()
|| (set->flags & ClientFlag::f_not_loaded)) {
setsToRequest.insert(set->id, set->access);
}
}
}
@@ -600,10 +643,10 @@ void Stickers::setsReceived(const QVector<MTPStickerSet> &data, int32 hash) {
auto &recent = getRecentPack();
for (auto it = sets.begin(); it != sets.end();) {
const auto set = it->second.get();
bool installed = (set->flags & MTPDstickerSet::Flag::f_installed_date);
bool featured = (set->flags & MTPDstickerSet_ClientFlag::f_featured);
bool special = (set->flags & MTPDstickerSet_ClientFlag::f_special);
bool archived = (set->flags & MTPDstickerSet::Flag::f_archived);
const auto installed = !!(set->flags & Flag::f_installed_date);
const auto featured = !!(set->flags & ClientFlag::f_featured);
const auto special = !!(set->flags & ClientFlag::f_special);
const auto archived = !!(set->flags & Flag::f_archived);
if (!installed) { // remove not mine sets from recent stickers
for (auto i = recent.begin(); i != recent.cend();) {
if (set->stickers.indexOf(i->first) >= 0) {
@@ -629,8 +672,14 @@ void Stickers::setsReceived(const QVector<MTPStickerSet> &data, int32 hash) {
api.requestStickerSets();
}
session().local().writeInstalledStickers();
if (writeRecent) session().saveSettings();
if (masksReceived) {
session().local().writeInstalledMasks();
} else {
session().local().writeInstalledStickers();
}
if (writeRecent) {
session().saveSettings();
}
const auto counted = Api::CountStickersHash(&session());
if (counted != hash) {
@@ -705,7 +754,8 @@ void Stickers::specialSetReceived(
auto dates = std::vector<TimeId>();
auto dateIndex = 0;
auto datesAvailable = (items.size() == usageDates.size())
&& (setId == CloudRecentSetId);
&& ((setId == CloudRecentSetId)
|| (setId == CloudRecentAttachedSetId));
auto customIt = sets.find(CustomSetId);
auto pack = StickersPack();
@@ -768,6 +818,16 @@ void Stickers::specialSetReceived(
}
session().local().writeRecentStickers();
} break;
case CloudRecentAttachedSetId: {
const auto counted = Api::CountRecentStickersHash(&session(), true);
if (counted != hash) {
LOG(("API Error: "
"received recent attached stickers hash %1 "
"while counted hash is %2"
).arg(hash, counted));
}
session().local().writeRecentMasks();
} break;
case FavedSetId: {
const auto counted = Api::CountFavedStickersHash(&session());
if (counted != hash) {
@@ -1193,13 +1253,17 @@ StickersSet *Stickers::feedSet(const MTPDstickerSet &data) {
const auto set = it->second.get();
auto changedFlags = (flags ^ set->flags);
if (changedFlags & MTPDstickerSet::Flag::f_archived) {
auto index = archivedSetsOrder().indexOf(set->id);
const auto masks = !!(set->flags & MTPDstickerSet::Flag::f_masks);
auto &archivedOrder = masks
? archivedMaskSetsOrderRef()
: archivedSetsOrderRef();
const auto index = archivedOrder.indexOf(set->id);
if (set->flags & MTPDstickerSet::Flag::f_archived) {
if (index < 0) {
archivedSetsOrderRef().push_front(set->id);
archivedOrder.push_front(set->id);
}
} else if (index >= 0) {
archivedSetsOrderRef().removeAt(index);
archivedOrder.removeAt(index);
}
}
return it->second.get();
@@ -1263,9 +1327,16 @@ StickersSet *Stickers::feedSetFull(const MTPmessages_StickerSet &data) {
}
}
const auto isMasks = !!(set->flags & MTPDstickerSet::Flag::f_masks);
if (pack.isEmpty()) {
int removeIndex = setsOrder().indexOf(set->id);
if (removeIndex >= 0) setsOrderRef().removeAt(removeIndex);
const auto removeIndex = (isMasks
? maskSetsOrder()
: setsOrder()).indexOf(set->id);
if (removeIndex >= 0) {
(isMasks
? maskSetsOrderRef()
: setsOrderRef()).removeAt(removeIndex);
}
sets.remove(set->id);
set = nullptr;
} else {
@@ -1299,7 +1370,9 @@ StickersSet *Stickers::feedSetFull(const MTPmessages_StickerSet &data) {
if (set) {
const auto isArchived = !!(set->flags & MTPDstickerSet::Flag::f_archived);
if (set->flags & MTPDstickerSet::Flag::f_installed_date) {
if (isMasks) {
session().local().writeInstalledMasks();
} else if (set->flags & MTPDstickerSet::Flag::f_installed_date) {
if (!isArchived) {
session().local().writeInstalledStickers();
}
@@ -1308,7 +1381,11 @@ StickersSet *Stickers::feedSetFull(const MTPmessages_StickerSet &data) {
session().local().writeFeaturedStickers();
}
if (wasArchived != isArchived) {
session().local().writeArchivedStickers();
if (isMasks) {
session().local().writeArchivedMasks();
} else {
session().local().writeArchivedStickers();
}
}
}
@@ -1329,10 +1406,8 @@ void Stickers::newSetReceived(const MTPmessages_StickerSet &data) {
LOG(("API Error: "
"updateNewStickerSet with archived flag."));
return;
} else if (s.is_masks()) {
return;
}
auto &order = setsOrderRef();
auto &order = s.is_masks() ? maskSetsOrderRef() : setsOrderRef();
int32 insertAtIndex = 0, currentIndex = order.indexOf(s.vid().v);
if (currentIndex != insertAtIndex) {
if (currentIndex > 0) {

View File

@@ -41,6 +41,7 @@ public:
// For cloud-stored recent stickers.
static constexpr auto CloudRecentSetId = 0xFFFFFFFFFFFFFFFCULL;
static constexpr auto CloudRecentAttachedSetId = 0xFFFFFFFFFFFFFFF9ULL;
// For cloud-stored faved stickers.
static constexpr auto FavedSetId = 0xFFFFFFFFFFFFFFFAULL;
@@ -48,12 +49,19 @@ public:
// For setting up megagroup sticker set.
static constexpr auto MegagroupSetId = 0xFFFFFFFFFFFFFFEFULL;
enum Recent {
Regular,
Attached,
};
void notifyUpdated();
[[nodiscard]] rpl::producer<> updated() const;
void notifyRecentUpdated();
[[nodiscard]] rpl::producer<> recentUpdated() const;
void notifyRecentUpdated(Recent recent = Recent::Regular);
[[nodiscard]] rpl::producer<Recent> recentUpdated() const;
void notifySavedGifsUpdated();
[[nodiscard]] rpl::producer<> savedGifsUpdated() const;
void notifyStickerSetInstalled(uint64 setId);
[[nodiscard]] rpl::producer<uint64> stickerSetInstalled() const;
void incrementSticker(not_null<DocumentData*> document);
@@ -72,6 +80,21 @@ public:
}
_lastRecentUpdate = update;
}
bool masksUpdateNeeded(crl::time now) const {
return updateNeeded(_lastMasksUpdate, now);
}
void setLastMasksUpdate(crl::time update) {
_lastMasksUpdate = update;
}
bool recentAttachedUpdateNeeded(crl::time now) const {
return updateNeeded(_lastRecentAttachedUpdate, now);
}
void setLastRecentAttachedUpdate(crl::time update) {
if (update) {
notifyRecentUpdated(Recent::Attached);
}
_lastRecentAttachedUpdate = update;
}
bool favedUpdateNeeded(crl::time now) const {
return updateNeeded(_lastFavedUpdate, now);
}
@@ -111,6 +134,12 @@ public:
StickersSetsOrder &setsOrderRef() {
return _setsOrder;
}
const StickersSetsOrder &maskSetsOrder() const {
return _maskSetsOrder;
}
StickersSetsOrder &maskSetsOrderRef() {
return _maskSetsOrder;
}
const StickersSetsOrder &featuredSetsOrder() const {
return _featuredSetsOrder;
}
@@ -123,6 +152,12 @@ public:
StickersSetsOrder &archivedSetsOrderRef() {
return _archivedSetsOrder;
}
const StickersSetsOrder &archivedMaskSetsOrder() const {
return _archivedMaskSetsOrder;
}
StickersSetsOrder &archivedMaskSetsOrderRef() {
return _archivedMaskSetsOrder;
}
const SavedGifs &savedGifs() const {
return _savedGifs;
}
@@ -196,18 +231,23 @@ private:
const not_null<Session*> _owner;
rpl::event_stream<> _updated;
rpl::event_stream<> _recentUpdated;
rpl::event_stream<Recent> _recentUpdated;
rpl::event_stream<> _savedGifsUpdated;
rpl::event_stream<uint64> _stickerSetInstalled;
crl::time _lastUpdate = 0;
crl::time _lastRecentUpdate = 0;
crl::time _lastFavedUpdate = 0;
crl::time _lastFeaturedUpdate = 0;
crl::time _lastSavedGifsUpdate = 0;
crl::time _lastMasksUpdate = 0;
crl::time _lastRecentAttachedUpdate = 0;
rpl::variable<int> _featuredSetsUnreadCount = 0;
StickersSets _sets;
StickersSetsOrder _setsOrder;
StickersSetsOrder _maskSetsOrder;
StickersSetsOrder _featuredSetsOrder;
StickersSetsOrder _archivedSetsOrder;
StickersSetsOrder _archivedMaskSetsOrder;
SavedGifs _savedGifs;
};

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