Compare commits

...

287 Commits

Author SHA1 Message Date
Ilya Fedin
6f1c1fd070 Fix packaged build 2020-02-25 00:50:26 +04:00
John Preston
ddf483012b Beta version 1.9.17.
- Spell checker on Windows 7.
- Bug fixes and other minor improvements.
2020-02-25 00:44:28 +04:00
Ilya Fedin
2b0e62dafe Follow hidding reply setting in native notifications on Linux, use system icon 2020-02-24 21:00:25 +04:00
Ilya Fedin
9979c220ce Multiple sizes for linux tray icon 2020-02-24 19:57:42 +04:00
John Preston
530b212f55 Remove _USING_V110_SDK71_ on Windows. 2020-02-24 19:55:48 +04:00
23rd
9fe64deea0 Updated lib_spellcheck. 2020-02-24 18:48:33 +03:00
23rd
0ca0930066 Updated context menu for spellchecker. 2020-02-24 18:48:33 +03:00
23rd
8734ebe4c4 Added auto download of new dictionary when input locale is changed. 2020-02-24 18:48:33 +03:00
23rd
bc6e1e7a0d Added new setting for automatic dictionaries download. 2020-02-24 18:48:33 +03:00
23rd
bb8aead078 Added sequential background dictionary loader.
- Moved the Loader from the dictionaries manager to the spellchecker
 common space as a DictLoader.
2020-02-24 18:48:33 +03:00
23rd
9daf362df6 Added label with state to "Manage dictionaries" button. 2020-02-24 18:48:33 +03:00
23rd
311678af80 Added ability to filter languages in dictionaries manager. 2020-02-24 18:48:33 +03:00
23rd
a0f995b134 Added ability to remove dictionary from context menu. 2020-02-24 18:48:33 +03:00
23rd
e9e9ea2d69 Added filter for removed dictionaries when dictionary box is closed. 2020-02-24 18:48:33 +03:00
23rd
4bd34b35ae Added button to advanced settings for dictionary management box. 2020-02-24 18:48:33 +03:00
23rd
9d1b93fe50 Moved spellchecker work from message_field to Spellchecker::Start. 2020-02-24 18:48:33 +03:00
23rd
9dee4e2d25 Added ability to download more than one dictionary at same time. 2020-02-24 18:48:33 +03:00
23rd
783269e256 Removed unnecessary Sets() function. 2020-02-24 18:48:33 +03:00
23rd
f598bf0b42 Replaced fake dictionary ids with real values. 2020-02-24 18:48:33 +03:00
23rd
039ed17683 Fixed saving of not loaded dictionaries. 2020-02-24 18:48:33 +03:00
23rd
62e0ced6a6 Imporved computing state of buttons in dictionaries manager. 2020-02-24 18:48:33 +03:00
23rd
f377ac54fd Slightly refactored spellchecker_common.cpp.
- Moved LocaleFromLangId to lib_spellcheck.
2020-02-24 18:48:33 +03:00
23rd
9d98682089 Removed unnecessary includes from emoji sets and dictionaries managers. 2020-02-24 18:48:33 +03:00
23rd
efdf5f1767 Moved producer of state description to CloudBlob. 2020-02-24 18:48:33 +03:00
23rd
8ca0b614d7 Moved loader of emoji sets and dictionaries to CloudBlob. 2020-02-24 18:48:33 +03:00
23rd
704dcc8d65 Moved emoji sets and dictionaries loader states to CloudBlob.
- Moved CloudBlob to second namespace.
2020-02-24 18:48:33 +03:00
23rd
9f4d05b04c Moved emoji sets from lib_ui. Added parent struct to storage_cloud_blob. 2020-02-24 18:48:33 +03:00
23rd
08cd7450ff Added storage/storage_cloud_blob.
- This file is needed to store same code parts related
 to management of dictionaries and emoji sets.
 - Moved extracting of zip files to storage_cloud_blob.
2020-02-24 18:48:33 +03:00
23rd
4b684a4926 Added spellchecker helper for common purposes. 2020-02-24 18:48:33 +03:00
23rd
65a7f2e7d8 Added dictionary management box. 2020-02-24 18:48:33 +03:00
23rd
26a45885ff Added updating of spell highlighter when enabled languages are changed. 2020-02-24 18:48:33 +03:00
23rd
4cc46f1ffa Added vector of enabled dictionaries to Main::Settings. 2020-02-24 18:48:33 +03:00
23rd
fcb5292a4f Added external_hunspell to CMake build. 2020-02-24 18:48:33 +03:00
23rd
1ca096e7ce Added auto caching for Github CI. 2020-02-24 18:07:09 +03:00
23rd
2b2ac2e48f Decreased packages installation time for Linux workflow. 2020-02-24 18:07:09 +03:00
Ilya Fedin
da14588235 Enable native Wayland support 2020-02-24 19:03:27 +04:00
John Preston
60612635ef Use QSaveFile to write sensitive settings / data. 2020-02-24 18:54:16 +04:00
John Preston
c2f58d3ab5 Fix GIFs with alpha display. 2020-02-24 17:48:23 +04:00
John Preston
5937b24799 Request dialog entry for unknown chat. 2020-02-24 16:35:13 +04:00
John Preston
8e222d3501 Fix closing of fullscreen GIFs by click. 2020-02-24 15:31:28 +04:00
John Preston
c3463dec63 Force zero unread count if read till end. 2020-02-24 14:54:00 +04:00
John Preston
f2ef109940 Make sure we request pending dialog entries. 2020-02-24 14:54:00 +04:00
John Preston
91fb9917bc Fix search in chat results loading. 2020-02-24 13:14:29 +04:00
John Preston
6ded5b74d0 Fix load requests cancel on history change. 2020-02-24 13:13:48 +04:00
John Preston
91a6632a1b Fix state assertion in reading requets. 2020-02-24 12:57:24 +04:00
Nicholas Guriev
f1c2d4fe3d Fix CMake version for snap build
* With CMake 1.17.0-rc1, Python package is broken.
2020-02-23 18:45:53 +04:00
Nicholas Guriev
23d958e457 Save build place on Linux workflow
* This tries to avoid "No space left on device" error.
 * Remove unneeded build cache directories.
 * Whenever possible, checkout only one Git commit.
2020-02-23 18:45:53 +04:00
John Preston
b84b1e71f7 Beta version 1.9.16: Crash fix. 2020-02-23 15:13:23 +04:00
John Preston
e2f037537f Beta version 1.9.16.
- Bug fixes and other minor improvements.
2020-02-23 12:58:45 +04:00
John Preston
a84c7e0b06 Don't apply entry from dialogs if postponed. 2020-02-23 12:56:33 +04:00
John Preston
f7144a55e2 Always clear history notifications when marking as read. 2020-02-23 12:48:39 +04:00
John Preston
496faef0b3 Fix crashes in fast simultaneous readings.
Fixes #7264, fixes #7259.
2020-02-23 11:54:18 +04:00
John Preston
50bf4dad36 Add local changelog for 1.9.15. 2020-02-22 14:47:30 +04:00
John Preston
0743e71ab6 Beta version 1.9.15.
- Mark new messages as read while scrolling down through them.
- Bug fixes and other minor improvements.
2020-02-21 21:33:32 +04:00
John Preston
c207a7c0d3 Fix simultaneous read history requests. 2020-02-21 19:05:57 +04:00
John Preston
d83cf0e560 Fix build with Clang. 2020-02-21 18:50:56 +04:00
John Preston
28032e5e0d Fix jump to a specific message. 2020-02-21 18:48:30 +04:00
John Preston
7f77db8c7f Fix request cancel in Histories. 2020-02-21 18:48:30 +04:00
John Preston
f9d02740aa Don't send same read request more than once. 2020-02-21 18:48:30 +04:00
John Preston
ec7a2dce2f Search through Histories. 2020-02-21 18:48:30 +04:00
John Preston
6f672ecdc3 Request history parts through Histories. 2020-02-21 18:48:30 +04:00
John Preston
818f5cd004 Send and delete messages through Histories. 2020-02-21 18:48:30 +04:00
John Preston
147e8cc467 Prepare for syncing read / write requests. 2020-02-21 18:48:30 +04:00
John Preston
db322cc19a Move requestDialogEntry to Histories. 2020-02-21 18:48:30 +04:00
John Preston
9bdcd08233 Don't read history for guest channels. 2020-02-21 18:48:30 +04:00
John Preston
a954b459b4 Fix crash on reading in support mode. 2020-02-21 18:48:30 +04:00
John Preston
f133210db3 Fix scroll first to unread then to end. 2020-02-21 18:48:30 +04:00
John Preston
ee8028cd53 From above the unread jump to unread by down button. 2020-02-21 18:48:30 +04:00
John Preston
f72cb979c0 Create unread bar when jumping to a message. 2020-02-21 18:48:30 +04:00
John Preston
49c4d35afa Improve working with unread bar. 2020-02-21 18:48:30 +04:00
John Preston
ee3e9af63a Remove counter from unread bar. 2020-02-21 18:48:30 +04:00
John Preston
1980c1004e Mark as read only in inited history view. 2020-02-21 18:48:29 +04:00
John Preston
a3f19c073b Fix 'reading' of an empty history. 2020-02-21 18:48:29 +04:00
John Preston
388173f0ad Fix delete+leave from a legacy group.
We first need to leave and receive a new message update about
us leaving the group and only after that remove the conversation
locally from the chats list, otherwise it reappears in the list.
2020-02-21 18:48:29 +04:00
John Preston
7cffb0ef9d First send delete request, then destroy locally. 2020-02-21 18:48:29 +04:00
John Preston
c8d2ac9583 Move message ownership to History. 2020-02-21 18:48:29 +04:00
John Preston
6357529901 Fix 90/270 degrees photo rotation.
Fixes #7197.
2020-02-21 18:48:29 +04:00
John Preston
b5dcd84513 Fix stop-auto-read when the system is idle. 2020-02-21 18:48:29 +04:00
John Preston
5b7f7ed70e Fix reading of currently opened chat. 2020-02-21 18:48:29 +04:00
John Preston
c04f3a7048 Don't replace local with server last message. 2020-02-21 18:48:29 +04:00
John Preston
32d93e2651 Fix several read requests together. 2020-02-21 18:48:29 +04:00
John Preston
9cccea9a87 All read history done through Data::Histories. 2020-02-21 18:48:29 +04:00
John Preston
b0e1ae3948 Prepare Data::Histories for requests interdependencies. 2020-02-21 18:48:29 +04:00
John Preston
70408f0e22 First version of reading-while-scrolling. 2020-02-21 18:48:29 +04:00
Ilya Fedin
db2aa7000a Fallback to non-panel icon when tray counter is disabled 2020-02-21 18:25:08 +04:00
John Preston
02bc999bd5 Update cmake_helpers. 2020-02-21 18:15:41 +04:00
Ilya Fedin
5bdc0db9e2 Generalize backward compatibility of linux launcher 2020-02-21 18:12:48 +04:00
Ilya Fedin
ca1623f34a Use XDG Desktop Portal only when variable is set 2020-02-21 18:11:24 +04:00
Ilya Fedin
bbc516cf43 Move TDESKTOP_DISABLE_DBUS_INTEGRATION to cmake_helpers 2020-02-21 18:08:55 +04:00
RadRussianRus
8128f851d1 Changed libvdpau source url 2020-02-19 12:02:42 +04:00
John Preston
742de6282f Version 1.9.14: Update CMake helpers. 2020-02-18 12:28:06 +04:00
Ilya Fedin
9d0ae61ee0 Revert tray icon size to 22 on Linux (except KDE) and fix tray counter disabling in KDE 2020-02-18 11:37:17 +04:00
John Preston
901a199035 Version 1.9.14.
- Bug fixes and other minor improvements.
2020-02-17 17:09:38 +04:00
John Preston
775d5b6dcc Use 64 byte alignment for ffmpeg frames.
Fixes #7225.
2020-02-17 16:44:08 +04:00
John Preston
c5c77ddb67 Rename Telegram Desktop to Telegram Lite. 2020-02-16 08:39:42 +04:00
John Preston
2f698de3b6 Update build scripts for Xcode 11 tools. 2020-02-16 08:30:51 +04:00
John Preston
82aa64ca0a Enable third column by default in Mac App Store build. 2020-02-15 22:45:50 +04:00
John Preston
3bb9e8c7eb Fix power outage logout only by fsync. 2020-02-14 18:12:08 +04:00
John Preston
555fe70df3 Don't delete old localstorage file copies. 2020-02-13 18:39:44 +04:00
John Preston
05c95a0307 Fix fullscreen rotate, fix rotate phrase. 2020-02-13 16:51:05 +04:00
John Preston
7bf2b607f9 Return glib event loop in static builds.
Regression was introduced in 3f5eaa8f0a.

Fixes problem with GTK file dialog running as modal windows. Fixes #7186.
2020-02-13 13:49:34 +04:00
John Preston
23bab2aeb9 Version 1.9.13: Update libtgvoip submodule. 2020-02-12 19:43:22 +04:00
John Preston
f5fdcc3af0 Enable secondary QR code login by default. 2020-02-12 19:43:22 +04:00
John Preston
5c079b0bbd Add additional QR code login debug logs. 2020-02-12 19:36:05 +04:00
John Preston
6bbcec0c23 Version 1.9.13.
- Bug fixes and other minor improvements.
2020-02-12 13:03:46 +04:00
John Preston
0beec6e335 Fix maximized window on secondary screen. 2020-02-12 13:03:08 +04:00
John Preston
a88423a33f Fix bad window rendering with maximize-on-launch.
I have no idea why MainWindow is ruined completely in case you call
MainWindow::show, MainWindow::setWindowState(maximized) and then
in the same context (without crl::on_main) create full screen viewer.
2020-02-12 12:09:17 +04:00
John Preston
1a2b2c15c5 Disable Wayland in Snap build. 2020-02-11 23:37:06 +04:00
John Preston
1210ba37c4 Version 1.9.12.
- Switch to Picture-in-Picture mode to watch your video
in a small window while doing something else.
- Change playback speed in the '...' menu when watching videos.
- Rotate photos and videos in the media viewer
using the rotate button in the bottom right corner.
2020-02-11 19:10:09 +04:00
John Preston
356e3c6907 Enable night mode by default on Mac App Store. 2020-02-11 16:00:19 +04:00
John Preston
3c5f8d08ad Fix radial animations in emoji download. 2020-02-11 14:12:42 +04:00
John Preston
75de655642 Fix #6804 once again. 2020-02-11 13:29:32 +04:00
Ilya Fedin
3574a9c874 Fix lost qgetenv in GetLauncherBasename 2020-02-11 13:14:22 +04:00
John Preston
bcd0fe38f0 Fix invisible passcode field.
Regression was introduced in fd8ae60dc.
2020-02-11 13:01:55 +04:00
Ilya Fedin
5171c0bd77 Fix crash when tray icon is disabled on macOS 2020-02-11 12:38:03 +04:00
Kirsan
4fececb94f Revert changes to notification replay. And replace user settings with Both (Enter and Ctrl+Enter) to polls. 2020-02-11 12:29:34 +04:00
Kirsan
9c562931a2 Respect user settings "Send by ..." for:
forward dialog
send file dialog
edit caption dialog
notification replay
schedule messages
new channel dialog
group description edit dialog
create poll dialog
rate call dialog
report bot dialog
support mode
2020-02-11 12:29:34 +04:00
John Preston
38bef584e1 Beta version 1.9.11.
- Bug fixes and other minor improvements.
2020-02-10 19:49:10 +04:00
Ilya Fedin
e62f727135 Fix creating of autostart launcher in snap 2020-02-10 19:30:41 +04:00
John Preston
a0e7ef61fc Update submodules. 2020-02-10 19:30:09 +04:00
John Preston
fd8ae60dc1 Change show order of MainWindow/OverlayWindow.
Fixes #6804.
2020-02-10 19:28:41 +04:00
John Preston
093c2887c3 Fix PiP on multi-monitor setup. 2020-02-10 18:45:23 +04:00
John Preston
770678e32a Fix crash in updates handling. 2020-02-10 15:58:58 +04:00
John Preston
07cc05f62e Fix loading thumbnails in videos in albums.
Once more fixes #6332.
2020-02-10 15:43:24 +04:00
Ilya Fedin
77719750ee Fix name of the snap desktop file
Use new switch for GSL
2020-02-10 15:34:03 +04:00
Ilya Fedin
fb2bbd87b7 Fix zlib linkage 2020-02-10 15:23:14 +04:00
Ilya Fedin
6206b6f843 Adapt indicator-application check for sandboxed environments
Fix quality loss in the tray icon image

Fix window showing by clicking on the tray icon on macOS

Fix tray icon displaying on KDE
2020-02-10 15:09:47 +04:00
Kai Uwe Broulik
9e3fa2e4bc Check action id when invoked
Makes it more resilient
2020-02-10 14:45:07 +04:00
John Preston
1f16d72667 Allow setSpeed() on non-active streaming player. 2020-02-06 13:27:21 +04:00
John Preston
6cf9157fb5 Fix jump-to-time from audio captions. 2020-02-06 13:19:33 +04:00
John Preston
76ff9a562e Fix build. 2020-02-05 22:06:40 +04:00
John Preston
ffeff09561 Beta version 1.9.10: Fix build for OS X 10.10-10.11. 2020-02-05 22:03:42 +04:00
John Preston
cc71bdce8f Beta version 1.9.10: Update lib_spellcheck. 2020-02-05 20:33:46 +04:00
John Preston
5d43df4f33 Beta version 1.9.10.
- Switch to the Picture-in-Picture mode
to watch your video in a small window.
- Change video playback speed in the playback controls '...' menu.
- Rotate photos and videos in the media viewer
using the rotate button in the bottom right corner.
2020-02-05 20:15:28 +04:00
John Preston
d29c3add79 Rotate video in PiP. 2020-02-05 20:04:40 +04:00
John Preston
4544b091a0 Allow rotating content in media viewer. 2020-02-05 19:37:39 +04:00
John Preston
91244d5211 Update cmake_helpers submodule. 2020-02-05 18:39:58 +04:00
John Preston
5cae7b3db1 Use system zlib on Linux, update submodules. 2020-02-05 18:39:47 +04:00
John Preston
702fe024c0 Fix a crash in reply data inconsistency. 2020-02-05 18:39:47 +04:00
John Preston
1ad0ff34df Don't overlap video with controls. 2020-02-05 18:39:47 +04:00
John Preston
282c502b71 Show playback time in PiP. 2020-02-05 18:39:47 +04:00
John Preston
d6e989cad5 Allow seek in PiP. 2020-02-05 18:39:47 +04:00
John Preston
23388b5705 Display playback progress in PiP. 2020-02-05 18:39:47 +04:00
John Preston
9ed56aa5d6 Good display of not-yet-started streaming in PiP. 2020-02-05 18:39:47 +04:00
John Preston
d9c5ab645c Fix radial loading in PiP widget. 2020-02-05 18:39:47 +04:00
John Preston
2cfb3c6755 Display dark background for PiP controls. 2020-02-05 18:39:47 +04:00
John Preston
e095c325b3 Move PiP controls to one corner. 2020-02-05 18:39:47 +04:00
John Preston
47f9978c46 Double click on PiP to switch back to fullscreen. 2020-02-05 18:39:47 +04:00
John Preston
2e1e13b843 Rename mediaview.style to media_view.style. 2020-02-05 18:39:47 +04:00
John Preston
7c2110c1f3 Closed alpha version 1.9.9.1. 2020-02-05 18:39:47 +04:00
John Preston
2a7ac6896c Update submodules. 2020-02-05 18:39:47 +04:00
John Preston
e13325ca22 Support menu with playback speed. 2020-02-05 18:39:47 +04:00
John Preston
e889a52f6f Improve design of video player and PiP controls. 2020-02-05 18:39:47 +04:00
John Preston
4d737b35da Fix PiP window on macOS. 2020-02-05 18:39:47 +04:00
John Preston
0fbd263562 Add shadow and round corners in PiP. 2020-02-05 18:39:47 +04:00
John Preston
f81f37505b Save video PiP window geometry. 2020-02-05 18:39:47 +04:00
John Preston
58dd33d8a2 Fix PiP window on macOS. 2020-02-05 18:39:47 +04:00
John Preston
f24f27a13c Add volume icon and playback speed info. 2020-02-05 18:39:47 +04:00
John Preston
87cc18aff8 Add video speed control slider. 2020-02-05 18:39:47 +04:00
John Preston
b88219902f Add sample PiP controls. 2020-02-05 18:39:47 +04:00
John Preston
63090fb75f Fix build for macOS. 2020-02-05 18:39:46 +04:00
John Preston
b5b520ab66 Make non-activatable PiP window. 2020-02-05 18:39:46 +04:00
John Preston
7a6052db81 Implement complex PiP movement. 2020-02-05 18:39:46 +04:00
John Preston
a73520c9d8 Allow resizing PiP. 2020-02-05 18:39:46 +04:00
John Preston
55b63cd2e3 Move PiP panel code to a separate class. 2020-02-05 18:39:46 +04:00
John Preston
612ee18a93 Snap PiP to screen edges. 2020-02-05 18:39:46 +04:00
John Preston
ca5c9271a3 First prototype of picture-in-picture player. 2020-02-05 18:39:46 +04:00
John Preston
9feea4a724 Don't blur video thumbnail if any side >= 240px.
Fixes #6332.
2020-02-05 18:39:46 +04:00
John Preston
3da99b6058 Add x11 plugin to Snap config. 2020-02-05 18:39:46 +04:00
Ilya Fedin
3b4dfa1381 Use LXQt's StatusNotifierItem implementation instead of appindicator 2020-02-05 18:38:34 +04:00
Ilya Fedin
1b1f9d9985 Adapt snap for system GSL 2020-02-04 21:22:04 +04:00
Ilya Fedin
3f5eaa8f0a Remove glib dependency from Qt 2020-02-04 21:19:51 +04:00
John Preston
28a567986d Try linking some libs dynamically on Linux. 2020-01-31 16:33:35 +03:00
John Preston
68027fd23e Add missing static plugin on Linux. 2020-01-31 16:05:03 +03:00
Ilya Fedin
2807c5ef19 Use system icon theme and increase icon size 2020-01-31 16:03:41 +03:00
Ilya Fedin
0477bda929 Use XDG Desktop Portal for file dialog 2020-01-31 16:02:11 +03:00
Ilya Fedin
d80b3fda7d Fix duplicate object path in SandboxAutostart and add some logging 2020-01-31 15:47:44 +03:00
Ilya Fedin
a831c1703a Split launcher filename detection to a function 2020-01-31 15:46:39 +03:00
John Preston
e6cec49646 Fix static libstdc++ linking in Updater.
Fixes #6939.
2020-01-29 14:54:25 +03:00
John Preston
74d848311b Replace str_const with base::const_string. 2020-01-29 12:47:50 +03:00
John Preston
c03df169b3 Attempt 2 to fix the snap build. 2020-01-29 10:07:01 +03:00
John Preston
9b19cba161 Version 1.9.9.
- Bug fixes and other minor improvements.
2020-01-28 20:45:49 +03:00
Ilya Fedin
9536a3c98e Use packaged build in the snap package 2020-01-28 20:38:12 +03:00
John Preston
f0de8131ec Improve phrases for scheduled messages. 2020-01-28 18:07:17 +03:00
John Preston
aa18cb64bc Catch more spellcheck exceptions.
Fixes #7086, hopefully fixes #7095.
2020-01-28 16:56:12 +03:00
23rd
7b36c91e0d Fixed insertion of 11th image from clipboard in SendFilesBox. 2020-01-28 16:56:03 +03:00
John Preston
fb0ceb110e Fix assertion violation in non-history song player. 2020-01-28 16:55:57 +03:00
RadRussianRus
20fbf0a655 Fixed formatting 2020-01-28 16:45:11 +03:00
RadRussianRus
46de86f1e4 Fix opening private linked channel from message 2020-01-28 16:45:11 +03:00
Ilya Fedin
05eabfd539 Add autostart support for linux 2020-01-28 16:44:27 +03:00
Ilya Fedin
74942cd06e Use QDBusConnection::connect to fix signal connection on old distributions 2020-01-28 16:43:23 +03:00
John Preston
24da40ef05 Fix -startintray on Ubuntu. 2020-01-28 11:41:05 +03:00
23rd
e7fbcce9d9 Added handler for SCHEDULE_STATUS_PRIVATE error when send file. 2020-01-28 11:20:36 +03:00
23rd
ef5055f4f3 Fixed missing reply cancel after sending scheduled message. 2020-01-28 11:20:36 +03:00
23rd
7cbc5ef902 Fixed scheduled sending text messages until online when user is online. 2020-01-28 11:20:36 +03:00
23rd
03d96a32f2 Fixed scheduled sending files until online when user is online. 2020-01-28 11:20:36 +03:00
John Preston
5406c17158 Handle only left click on button.
Fixes #7073.
2020-01-28 09:46:21 +03:00
John Preston
29c6228616 Add quiz phrase to langpack. 2020-01-28 09:33:36 +03:00
23rd
3f4a44c828 Fixed wrong checking of index in auto-closer Github Action. 2020-01-27 20:20:05 +03:00
RadRussianRus
327c9caed7 Fix opening hashtag when in archive 2020-01-27 19:56:30 +03:00
Nicholas Guriev
07560188cf Perform test for additional libatomic 2020-01-27 19:41:56 +03:00
23rd
2c64676415 Removed Travis CI in favor of Github CI.
Thanks Auchri for the work he did, but we are moving on with Github CI!
2020-01-24 21:53:45 +03:00
23rd
29ddd88a5d Added Github CI for Linux. Added new build status badge. 2020-01-24 21:53:45 +03:00
23rd
543dc80b0b Reduced cache size in Github CI for Windows and macOS.
- Other minor improvements.
2020-01-24 21:53:45 +03:00
John Preston
2008b1beea Version 1.9.8.
- Bug fixes and other minor improvements.
2020-01-24 18:30:59 +03:00
John Preston
9aa597d6f0 Fix validation of created poll. 2020-01-24 18:29:53 +03:00
Ilya Fedin
1d85416434 Remove unneeded include from QtDBus notifications 2020-01-24 18:06:58 +03:00
John Preston
bb64285c3a Always show Create button, show error messages.
Fixes #7058, fixes #7059.
2020-01-24 17:45:05 +03:00
John Preston
6a581830f4 Enable IPO by CMake option. 2020-01-24 17:11:30 +03:00
John Preston
f1d155c3f6 Added fireworks animation for right quiz answer. 2020-01-24 17:01:25 +03:00
John Preston
66e3b529b7 Fix visual glitches in bubble shaking. 2020-01-24 17:01:25 +03:00
Ilya Fedin
04cfd598e2 Use appindicator in flatpak and fix desktop environment detection, add pavucontrol-qt 2020-01-24 16:51:50 +03:00
Vitaly Zaitsev
b94c8436eb Added missing Qt header. Fixed clang build.
Signed-off-by: Vitaly Zaitsev <vitaly@easycoding.org>
2020-01-24 15:35:15 +03:00
John Preston
dbfc555d9c Support shaking bubble for wrong quiz answer. 2020-01-24 11:49:12 +03:00
John Preston
5aacf867cd Correctly apply 'min' updates in polls. 2020-01-24 10:31:18 +03:00
John Preston
2f9db1d069 Fix signedness compare warning. 2020-01-24 10:30:59 +03:00
John Preston
3c022b893a Show correct incoming quiz votes in green. 2020-01-24 10:27:08 +03:00
John Preston
b74adc5311 Allow viewing round videos in scheduled messages. 2020-01-23 15:37:12 +03:00
John Preston
ed8b237364 Fix assertion violation in poll create box. 2020-01-23 15:36:53 +03:00
John Preston
7170808d2b Fix unloading thumbnails in videos.
Fixes #6332.
2020-01-23 14:02:18 +03:00
John Preston
578a80dfb8 Remove AppVeyor CI in favor of GitHub. 2020-01-23 13:48:15 +03:00
John Preston
d72a5e0d3d Fix build for Mac App Store. 2020-01-23 13:14:19 +03:00
John Preston
2699af5d5a Version 1.9.7.
- Create three new kinds of polls.
- See who voted for what in polls with visible votes.
- Vote for several options in polls that allow multiple answers.
- Guess the correct answer in quiz-style polls.
- Explore various ways of combining the different poll options.
- Add polls from the '...' menu in any group or channel.
- Use bots like @QuizBot to create quizzes
with several questions and media attachments.
- Schedule messages to be sent when your recipient comes online
(only works if you know their online status).
2020-01-23 10:46:57 +03:00
23rd
8298caddc3 Removing scheduling messages until online for those who hide their online. 2020-01-23 10:46:57 +03:00
John Preston
1bcde1b55c Pass cdn_supported flag to upload.getFile. 2020-01-23 10:46:57 +03:00
23rd
5d492bfe93 Added Github Action that closes issue if it was created for TG macOS. 2020-01-23 10:46:57 +03:00
John Preston
655b3d7c50 Update submodules. 2020-01-23 10:46:57 +03:00
John Preston
e4278745d5 Closed alpha version 1.9.6.1. 2020-01-23 10:09:34 +03:00
23rd
e266dc153b Implemented ability to add more than 1 file to albums via SendFilesBox. 2020-01-23 10:09:34 +03:00
John Preston
a482b744d2 Add a ripple animation for polls bottom button. 2020-01-23 10:09:34 +03:00
John Preston
d5bf742912 Improve polls layout. 2020-01-23 10:09:34 +03:00
John Preston
4346aecb61 Add a separate string for a pinned quiz. 2020-01-23 10:09:34 +03:00
John Preston
bdfa080701 Fix build for Windows. 2020-01-23 10:09:34 +03:00
23rd
465a42a825 Added ability to customize shortcuts for sending messages. 2020-01-23 10:09:34 +03:00
23rd
7d519990b2 Added ability to customize shortcuts for silent or scheduled messages. 2020-01-23 10:09:34 +03:00
23rd
19fd3a15e1 Updated range-v3 to 0.10.0. 2020-01-23 10:09:34 +03:00
23rd
12250676f9 Added restoring of scroll position after edit items in SendFilesBox. 2020-01-23 10:09:34 +03:00
23rd
a1bb9cbb2b Added ability to delete and edit files in SendFilesBox. 2020-01-23 10:09:34 +03:00
23rd
836df873f0 Added ability to delete and edit separate media in SendFilesBox. 2020-01-23 10:09:33 +03:00
23rd
58cc8fc08b Removed IconButtons from album items in SendFilesBox. 2020-01-23 10:09:33 +03:00
23rd
d1d5312ead Moved searching of sticker mimes to single place. 2020-01-23 10:09:33 +03:00
23rd
f3595e379c Added processing of small album thumb widths in SendFilesBox. 2020-01-23 10:09:33 +03:00
23rd
5f8c2f90ff Added button to add new files in SendFilesBox to album only. 2020-01-23 10:09:33 +03:00
23rd
abc7b8364c Slightly refactored code in SendFilesBox. 2020-01-23 10:09:33 +03:00
23rd
627a105ba9 Moved album files filter to FileDialog namespace. 2020-01-23 10:09:33 +03:00
23rd
faef5d8af6 Added buttons to delete and edit album items in SendFilesBox. 2020-01-23 10:09:33 +03:00
23rd
cf1dc3df78 Added ability to edit album items in send files box via FileDialog. 2020-01-23 10:09:33 +03:00
23rd
a26e4eee18 Split some things into different methods in SendFilesBox. 2020-01-23 10:09:33 +03:00
23rd
efa4deef6a Moved media editing via FileDialog to separate static method. 2020-01-23 10:09:33 +03:00
23rd
7e00930319 Added ability to delete items from album in SendFilesBox. 2020-01-23 10:09:33 +03:00
23rd
c08b2ae3df Added ability to schedule messages to be sent when user comes online.
Pro tip: Hold Ctrl key to send a silent scheduled message!
2020-01-23 10:09:33 +03:00
23rd
8ebbeb5274 Fixed display of scheduled until online message dates. 2020-01-23 10:09:33 +03:00
John Preston
c3b01d8573 Hide multiple answers if only quiz is allowed. 2020-01-23 10:09:33 +03:00
John Preston
43d8dedec4 Fix collapse after full results are loaded. 2020-01-23 10:09:33 +03:00
John Preston
cd97b649ff Improve phrases for polls. 2020-01-23 10:09:33 +03:00
John Preston
ac650b08fd Allow collapsing expanded poll results. 2020-01-23 10:09:33 +03:00
John Preston
e6c005dcba Fix poll result header on scroll top. 2020-01-23 10:09:33 +03:00
John Preston
5acb7448b7 Add loading rows placeholder. 2020-01-23 10:09:33 +03:00
John Preston
80168a58a7 Improve poll results loading. 2020-01-23 10:09:33 +03:00
John Preston
ebae0d71b8 Save poll results state when viewing profiles. 2020-01-23 10:09:33 +03:00
John Preston
8c11e1724a Move poll results to a Info-like layer. 2020-01-23 10:09:33 +03:00
John Preston
d0597407d8 Support creating polls from bot keyboards. 2020-01-23 10:09:33 +03:00
John Preston
c3aa2abe11 Fix multiple answers polls voting. 2020-01-23 10:09:33 +03:00
John Preston
3e0b2f5553 Add PollResultsBox. 2020-01-23 10:09:33 +03:00
John Preston
3d1275e19a Disable forwarding public polls to channels. 2020-01-23 10:09:33 +03:00
John Preston
d72d1aabe6 Disable creating public polls in channels. 2020-01-23 10:09:33 +03:00
John Preston
aac6d0df27 Improve quiz phrases. 2020-01-23 10:09:33 +03:00
John Preston
f700220ec1 Create polls with different settings. 2020-01-23 10:09:33 +03:00
John Preston
04d9b93e17 Implement poll creation settings UI. 2020-01-23 10:09:33 +03:00
John Preston
989fad8554 Add poll option select animation. 2020-01-23 10:09:33 +03:00
John Preston
2981a16e17 Allow sending multiple votes in a poll. 2020-01-23 10:09:33 +03:00
John Preston
afff7634f9 Display last voters userpics. 2020-01-23 10:09:33 +03:00
John Preston
95b2886bad Display correct poll subtitle and quiz answer. 2020-01-23 10:09:33 +03:00
John Preston
d57905c2b3 Update API scheme to layer 109. 2020-01-23 10:09:33 +03:00
Ilya Fedin
ffe037f9f1 Fix single instance socket path for compatibility with flatpak 2020-01-23 09:58:12 +03:00
Sergey
3a748e20c2 Fix Github CI MacOS artifacts 2020-01-23 09:57:06 +03:00
John Preston
b0c2ed839d Use c++17 instead of c++2a on GCC.
For now GCC9 and c++2a crashes with ICE somewhere deep in range-v3.
2020-01-22 17:25:04 +03:00
John Preston
eee252bb74 Filter out Unicode tag symbols from document names.
Fixes #7005.
2020-01-22 17:24:42 +03:00
Vitaly Zaitsev
59a8acc667 Implemented installation support for GNU/Linux.
Signed-off-by: Vitaly Zaitsev <vitaly@easycoding.org>
2020-01-22 16:35:35 +03:00
John Preston
960f50824d Fix one more crash in CDN file download.
The sessionTimedOut could remove a session right between a request
for a file part and a request for additional cdn file hashes.

In this case requestData.sessionIndex was not updated and this
was leading to an assertion violation in changeRequestedAmount.
2020-01-22 16:13:50 +03:00
John Preston
63020ec302 Fix radial animation ending.
Fixes (2) from #6975.
2020-01-22 11:55:27 +03:00
John Preston
71d4563b9d Use 'telegramdesktop' as a default desktop file base name. 2020-01-22 11:55:27 +03:00
Ilya Fedin
98bfd7370d Make TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME and TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION respect DESKTOP_APP_USE_PACKAGED 2020-01-22 10:06:56 +03:00
Federico Armellini
45a81a5016 Fix changelog year 2020 2020-01-22 10:06:24 +03:00
John Preston
389fb0c4e9 Fix GIF real parent refresh after sending.
Fixes #6943.
2020-01-22 09:53:59 +03:00
John Preston
fc72fe3a78 Version 1.9.6.
- Bug fixes and other minor improvements.
2020-01-22 02:16:47 +03:00
John Preston
5f646dd125 Fix strange crash on Windows. 2020-01-22 02:16:38 +03:00
316 changed files with 13264 additions and 5388 deletions

View File

@@ -1,86 +0,0 @@
@echo off
IF "%BUILD_DIR%"=="" SET BUILD_DIR=C:\TBuild
SET LIB_DIR=%BUILD_DIR%\Libraries
SET SRC_DIR=%BUILD_DIR%\tdesktop
SET QT_VERSION=5_6_2
call:configureBuild
call:getDependencies
call:setupGYP
cd %SRC_DIR%
echo Finished!
GOTO:EOF
:: FUNCTIONS
:logInfo
echo [INFO] %~1
GOTO:EOF
:logError
echo [ERROR] %~1
GOTO:EOF
:getDependencies
call:logInfo "Clone dependencies repository"
git clone -q --depth 1 --branch master https://github.com/telegramdesktop/dependencies_windows.git %LIB_DIR%
cd %LIB_DIR%
git clone --depth 1 --branch 0.9.1 https://github.com/ericniebler/range-v3
if exist prepare.bat (
call prepare.bat
) else (
call:logError "Error cloning dependencies, trying again"
rmdir %LIB_DIR% /S /Q
call:getDependencies
)
GOTO:EOF
:setupGYP
call:logInfo "Setup GYP/Ninja and generate VS solution"
cd %LIB_DIR%
git clone https://github.com/telegramdesktop/gyp.git
cd gyp
git checkout tdesktop
SET PATH=%PATH%;%BUILD_DIR%\Libraries\gyp;%BUILD_DIR%\Libraries\ninja;
cd %SRC_DIR%
git submodule init
git submodule update
cd %SRC_DIR%\Telegram
call gyp\refresh.bat --api-id 17349 --api-hash 344583e45741c457fe1862106095a5eb --ci-build
GOTO:EOF
:configureBuild
call:logInfo "Configuring build"
call:logInfo "Build version: %BUILD_VERSION%"
set TDESKTOP_BUILD_DEFINES=
echo %BUILD_VERSION% | findstr /C:"disable_register_custom_scheme">nul && (
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME
)
echo %BUILD_VERSION% | findstr /C:"disable_crash_reports">nul && (
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,DESKTOP_APP_DISABLE_CRASH_REPORTS
)
echo %BUILD_VERSION% | findstr /C:"disable_network_proxy">nul && (
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,TDESKTOP_DISABLE_NETWORK_PROXY
)
echo %BUILD_VERSION% | findstr /C:"disable_desktop_file_generation">nul && (
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION
)
echo %BUILD_VERSION% | findstr /C:"disable_gtk_integration">nul && (
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,TDESKTOP_DISABLE_GTK_INTEGRATION
)
if not "%TDESKTOP_BUILD_DEFINES%" == "" (
set "TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES:~1%"
)
call:logInfo "Build Defines: %TDESKTOP_BUILD_DEFINES%"
GOTO:EOF

103
.github/workflows/issue_closer.yml vendored Normal file
View File

@@ -0,0 +1,103 @@
name: Issue closer.
on:
issues:
types: opened
jobs:
comment:
runs-on: ubuntu-latest
steps:
- name: Get the latest version.
run: |
tag=$(git ls-remote --tags git://github.com/$GITHUB_REPOSITORY | cut -f 2 | tail -n1)
echo $tag
echo ::set-env name=LATEST_TAG::$tag
- name: Check a version from an issue.
uses: actions/github-script@0.4.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
let errorStr = "Version not found.";
let item1 = "Version of Telegram Desktop";
let item2 = "Used theme";
let body = context.payload.issue.body;
console.log("Body of issue:\n" + body);
let index1 = body.indexOf(item1);
let index2 = body.indexOf(item2);
index2 = (index2 == -1) ? Number.MAX_SAFE_INTEGER : index2;
console.log("Index 1: " + index1);
console.log("Index 2: " + index2);
if (index1 == -1) {
console.log(errorStr);
return;
}
function parseVersion(str) {
let pattern = /[0-9]\.[0-9][0-9.]{0,}/g;
return str.match(pattern);
}
function firstNum(version) {
return version[0].split(".")[0];
}
let issueVer = parseVersion(body.substring(index1 + item1.length, index2));
if (issueVer == undefined) {
console.log(errorStr);
return;
}
console.log("Version from issue: " + issueVer[0]);
let latestVer = parseVersion(process.env.LATEST_TAG);
if (latestVer == undefined) {
console.log(errorStr);
return;
}
console.log("Version from tags: " + latestVer[0]);
let issueNum = firstNum(issueVer);
let latestNum = firstNum(latestVer);
if (issueNum <= latestNum && issueNum < 5) {
console.log("Seems the version of this issue is fine!");
return;
}
let message = `
Sorry, but according to the version you specify in this issue, \
you are using the [Telegram for macOS](https://macos.telegram.org), \
not the [Telegram Desktop](https://desktop.telegram.org).
You can report your issue to [the group](https://t.me/macswift) \
or to [the repository of Telegram for macOS](https://github.com/overtake/TelegramSwift).
If I made a mistake and closed your issue wrongly, please reopen it. Thanks!
`;
let params = {
owner: context.issue.owner,
repo: context.issue.repo,
issue_number: context.issue.number
};
github.issues.createComment({
...params,
body: message
});
github.issues.addLabels({
...params,
labels: ['TG macOS Swift']
});
github.issues.update({
...params,
state: 'closed'
});

532
.github/workflows/linux.yml vendored Normal file
View File

@@ -0,0 +1,532 @@
name: Linux.
on:
push:
paths-ignore:
- 'docs/**'
- '*.md'
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
jobs:
linux:
name: Ubuntu 14.04
runs-on: ubuntu-latest
container: ubuntu:trusty
strategy:
matrix:
defines:
- ""
env:
GIT: "https://github.com"
QT: "5_12_5"
QT_PREFIX: "/usr/local/desktop-app/Qt-5.12.5"
OPENSSL_VER: "1_1_1"
OPENSSL_PREFIX: "/usr/local/desktop-app/openssl-1.1.1"
CMAKE_VER: "3.16.3"
UPLOAD_ARTIFACT: "false"
ONLY_CACHE: "false"
MANUAL_CACHING: "6"
DOC_PATH: "docs/building-cmake.md"
AUTO_CACHING: "1"
steps:
- name: Get repository name.
run: echo ::set-env name=REPO_NAME::${GITHUB_REPOSITORY##*/}
- name: Clone.
uses: actions/checkout@v1
with:
submodules: recursive
- name: Disable man for further package installs.
run: |
cfgFile="/etc/dpkg/dpkg.cfg.d/no_man"
sudo touch $cfgFile
p() {
sudo echo "path-exclude=/usr/share/$1/*" >> $cfgFile
}
p man
p locale
p doc
- name: First set up.
shell: bash
run: |
cd ..
mv $REPO_NAME temp
mkdir $REPO_NAME
mv temp $REPO_NAME/$REPO_NAME
cd $REPO_NAME
sudo apt-get update
sudo apt-get install software-properties-common -y && \
sudo apt-get install git libexif-dev liblzma-dev libz-dev libssl-dev \
libgtk2.0-dev libice-dev libsm-dev libicu-dev libdrm-dev dh-autoreconf \
autoconf automake build-essential libxml2-dev libass-dev libfreetype6-dev \
libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev \
libvorbis-dev libenchant-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev \
libxcb-xfixes0-dev libxcb-keysyms1-dev libxcb-icccm4-dev libatspi2.0-dev \
libxcb-render-util0-dev libxcb-util0-dev libxcb-xkb-dev libxrender-dev \
libasound-dev libpulse-dev libxcb-sync0-dev libxcb-randr0-dev libegl1-mesa-dev \
libx11-xcb-dev libffi-dev libncurses5-dev pkg-config texi2html bison yasm \
zlib1g-dev xutils-dev python-xcbgen chrpath gperf wget -y --force-yes && \
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
sudo apt-get update && \
sudo apt-get install gcc-8 g++-8 -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 60 && \
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 60 && \
sudo update-alternatives --config gcc && \
sudo add-apt-repository --remove ppa:ubuntu-toolchain-r/test -y
gcc --version
gcc --version > CACHE_KEY.txt
echo $MANUAL_CACHING >> CACHE_KEY.txt
if [ "$AUTO_CACHING" == "1" ]; then
thisFile=$REPO_NAME/.github/workflows/linux.yml
echo `md5sum $thisFile | cut -c -32` >> CACHE_KEY.txt
fi
md5cache=$(md5sum CACHE_KEY.txt | cut -c -32)
echo ::set-env name=CACHE_KEY::$md5cache
mkdir -p Libraries
cd Libraries
echo ::set-env name=LibrariesPath::`pwd`
- name: Range-v3.
run: |
echo "Find necessary branch from doc."
cloneRange=$(grep -A 1 "range-v3" $REPO_NAME/$DOC_PATH | sed -n 1p)
cd $LibrariesPath
echo $cloneRange
eval $cloneRange
- name: Patches.
run: |
echo "Find necessary commit from doc."
checkoutCommit=$(grep -A 1 "cd patches" $REPO_NAME/$DOC_PATH | sed -n 2p)
cd $LibrariesPath
git clone $GIT/desktop-app/patches.git
cd patches
eval $checkoutCommit
- name: CMake.
run: |
cd $LibrariesPath
file=cmake-$CMAKE_VER-Linux-x86_64.sh
wget $GIT/Kitware/CMake/releases/download/v$CMAKE_VER/$file
sudo mkdir /opt/cmake
sudo sh $file --prefix=/opt/cmake --skip-license
sudo ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake
cmake --version
- name: Opus cache.
id: cache-opus
uses: actions/cache@v1
with:
path: ${{ env.LibrariesPath }}/opus
key: ${{ runner.OS }}-opus-${{ env.CACHE_KEY }}
- name: Opus.
if: steps.cache-opus.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
git clone -b v1.3 --depth=1 $GIT/xiph/opus
cd opus
./autogen.sh
./configure
make -j$(nproc)
- name: Opus install.
run: |
cd $LibrariesPath/opus
sudo make install
- name: Libva.
run: |
cd $LibrariesPath
git clone $GIT/01org/libva.git
cd libva
./autogen.sh --enable-static
make -j$(nproc)
sudo make install
cd ..
rm -rf libva
- name: Libvdpau.
run: |
cd $LibrariesPath
git clone -b libvdpau-1.2 --depth=1 https://gitlab.freedesktop.org/vdpau/libvdpau.git
cd libvdpau
./autogen.sh --enable-static
make -j$(nproc)
sudo make install
cd ..
rm -rf libvdpau
- name: FFmpeg cache.
id: cache-ffmpeg
uses: actions/cache@v1
with:
path: ${{ env.LibrariesPath }}/ffmpeg-cache
key: ${{ runner.OS }}-ffmpeg-${{ env.CACHE_KEY }}
- name: FFmpeg build.
if: steps.cache-ffmpeg.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
git clone --branch release/3.4 $GIT/FFmpeg/FFmpeg ffmpeg
cd ffmpeg
./configure --prefix=$LibrariesPath/ffmpeg-cache \
--enable-protocol=file --enable-libopus \
--disable-programs \
--disable-doc \
--disable-network \
--disable-everything \
--enable-hwaccel=h264_vaapi \
--enable-hwaccel=h264_vdpau \
--enable-hwaccel=mpeg4_vaapi \
--enable-hwaccel=mpeg4_vdpau \
--enable-decoder=aac \
--enable-decoder=aac_at \
--enable-decoder=aac_fixed \
--enable-decoder=aac_latm \
--enable-decoder=aasc \
--enable-decoder=alac \
--enable-decoder=alac_at \
--enable-decoder=flac \
--enable-decoder=gif \
--enable-decoder=h264 \
--enable-decoder=h264_vdpau \
--enable-decoder=hevc \
--enable-decoder=mp1 \
--enable-decoder=mp1float \
--enable-decoder=mp2 \
--enable-decoder=mp2float \
--enable-decoder=mp3 \
--enable-decoder=mp3adu \
--enable-decoder=mp3adufloat \
--enable-decoder=mp3float \
--enable-decoder=mp3on4 \
--enable-decoder=mp3on4float \
--enable-decoder=mpeg4 \
--enable-decoder=mpeg4_vdpau \
--enable-decoder=msmpeg4v2 \
--enable-decoder=msmpeg4v3 \
--enable-decoder=opus \
--enable-decoder=pcm_alaw \
--enable-decoder=pcm_alaw_at \
--enable-decoder=pcm_f32be \
--enable-decoder=pcm_f32le \
--enable-decoder=pcm_f64be \
--enable-decoder=pcm_f64le \
--enable-decoder=pcm_lxf \
--enable-decoder=pcm_mulaw \
--enable-decoder=pcm_mulaw_at \
--enable-decoder=pcm_s16be \
--enable-decoder=pcm_s16be_planar \
--enable-decoder=pcm_s16le \
--enable-decoder=pcm_s16le_planar \
--enable-decoder=pcm_s24be \
--enable-decoder=pcm_s24daud \
--enable-decoder=pcm_s24le \
--enable-decoder=pcm_s24le_planar \
--enable-decoder=pcm_s32be \
--enable-decoder=pcm_s32le \
--enable-decoder=pcm_s32le_planar \
--enable-decoder=pcm_s64be \
--enable-decoder=pcm_s64le \
--enable-decoder=pcm_s8 \
--enable-decoder=pcm_s8_planar \
--enable-decoder=pcm_u16be \
--enable-decoder=pcm_u16le \
--enable-decoder=pcm_u24be \
--enable-decoder=pcm_u24le \
--enable-decoder=pcm_u32be \
--enable-decoder=pcm_u32le \
--enable-decoder=pcm_u8 \
--enable-decoder=pcm_zork \
--enable-decoder=vorbis \
--enable-decoder=wavpack \
--enable-decoder=wmalossless \
--enable-decoder=wmapro \
--enable-decoder=wmav1 \
--enable-decoder=wmav2 \
--enable-decoder=wmavoice \
--enable-encoder=libopus \
--enable-parser=aac \
--enable-parser=aac_latm \
--enable-parser=flac \
--enable-parser=h264 \
--enable-parser=hevc \
--enable-parser=mpeg4video \
--enable-parser=mpegaudio \
--enable-parser=opus \
--enable-parser=vorbis \
--enable-demuxer=aac \
--enable-demuxer=flac \
--enable-demuxer=gif \
--enable-demuxer=h264 \
--enable-demuxer=hevc \
--enable-demuxer=m4v \
--enable-demuxer=mov \
--enable-demuxer=mp3 \
--enable-demuxer=ogg \
--enable-demuxer=wav \
--enable-muxer=ogg \
--enable-muxer=opus
make -j$(nproc)
sudo make install
cd ..
rm -rf ffmpeg
- name: FFmpeg install.
run: |
cd $LibrariesPath
#List of files from cmake/external/ffmpeg/CMakeLists.txt.
copyLib() {
mkdir -p ffmpeg/$1
yes | cp -i ffmpeg-cache/lib/$1.a ffmpeg/$1/$1.a
}
copyLib libavformat
copyLib libavcodec
copyLib libswresample
copyLib libswscale
copyLib libavutil
sudo cp -R ffmpeg-cache/. /usr/local/
- name: PortAudio.
run: |
cd $LibrariesPath
git clone https://git.assembla.com/portaudio.git
cd portaudio
git checkout 396fe4b669
./configure
make -j$(nproc)
sudo make install
cd ..
rm -rf portaudio
- name: OpenAL Soft.
run: |
cd $LibrariesPath
git clone -b openal-soft-1.19.1 --depth=1 $GIT/kcat/openal-soft.git
cd openal-soft/build
cmake -D LIBTYPE:STRING=STATIC ..
make -j$(nproc)
sudo make install
cd -
rm -rf openal-soft
- name: OpenSSL cache.
id: cache-openssl
uses: actions/cache@v1
with:
path: ${{ env.LibrariesPath }}/openssl-cache
key: ${{ runner.OS }}-${{ env.OPENSSL_VER }}-${{ env.CACHE_KEY }}
- name: OpenSSL build.
if: steps.cache-openssl.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
git clone -b OpenSSL_${OPENSSL_VER}-stable --depth=1 \
$GIT/openssl/openssl openssl_${OPENSSL_VER}
cd openssl_${OPENSSL_VER}
./config --prefix=$LibrariesPath/openssl-cache
make -j$(nproc)
sudo make install
cd ..
rm -rf openssl_${OPENSSL_VER}
- name: OpenSSL install.
run: |
cd $LibrariesPath
sudo mkdir -p $OPENSSL_PREFIX
sudo cp -R openssl-cache/. $OPENSSL_PREFIX/
- name: Libxkbcommon.
run: |
cd $LibrariesPath
git clone -b xkbcommon-0.8.4 --depth=1 $GIT/xkbcommon/libxkbcommon.git
cd libxkbcommon
./autogen.sh
make -j$(nproc)
sudo make install
cd ..
rm -rf libxkbcommon
- name: Libwayland.
run: |
cd $LibrariesPath
git clone -b 1.16 https://gitlab.freedesktop.org/wayland/wayland
cd wayland
./autogen.sh --enable-static --disable-documentation
make -j$(nproc)
sudo make install
cd ..
rm -rf wayland
- name: Qt 5.12.5 cache.
id: cache-qt
uses: actions/cache@v1
with:
path: ${{ env.LibrariesPath }}/qt-cache
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_5_12_5.diff') }}
- name: Qt 5.12.5 build.
if: steps.cache-qt.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
git clone -b v5.12.5 --depth=1 git://code.qt.io/qt/qt5.git qt_${QT}
cd qt_${QT}
perl init-repository --module-subset=qtbase,qtwayland,qtimageformats,qtsvg
git submodule update qtbase qtwayland qtimageformats qtsvg
cd qtbase
git apply ../../patches/qtbase_${QT}.diff
cd src/plugins/platforminputcontexts
git clone $GIT/desktop-app/fcitx.git
git clone $GIT/desktop-app/hime.git
git clone $GIT/desktop-app/nimf.git
cd ../../../..
./configure -prefix "$LibrariesPath/qt-cache" \
-release \
-force-debug-info \
-opensource \
-confirm-license \
-qt-zlib \
-qt-libpng \
-qt-libjpeg \
-qt-harfbuzz \
-qt-pcre \
-qt-xcb \
-system-freetype \
-fontconfig \
-no-gtk \
-static \
-dbus-runtime \
-openssl-linked \
-I "$OPENSSL_PREFIX/include" OPENSSL_LIBS="$OPENSSL_PREFIX/lib/libssl.a $OPENSSL_PREFIX/lib/libcrypto.a -ldl -lpthread" \
-nomake examples \
-nomake tests
make -j$(nproc)
sudo make install
cd ..
rm -rf qt_${QT}
- name: Qt 5.12.5 install.
run: |
cd $LibrariesPath
sudo mkdir -p $QT_PREFIX
sudo cp -R qt-cache/. $QT_PREFIX/
- name: Breakpad cache.
id: cache-breakpad
uses: actions/cache@v1
with:
path: ${{ env.LibrariesPath }}/breakpad-cache
key: ${{ runner.OS }}-breakpad-${{ env.CACHE_KEY }}
- name: Breakpad clone.
run: |
cd $LibrariesPath
git clone https://chromium.googlesource.com/breakpad/breakpad
cd breakpad
git checkout bc8fb886
git clone https://chromium.googlesource.com/linux-syscall-support src/third_party/lss
cd src/third_party/lss
git checkout a91633d1
- name: Breakpad build.
if: steps.cache-breakpad.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
BreakpadCache=$LibrariesPath/breakpad-cache
git clone https://chromium.googlesource.com/external/gyp
cd gyp
git checkout 9f2a7bb1
git apply ../patches/gyp.diff
cd ..
cd breakpad
./configure --prefix=$BreakpadCache
make -j$(nproc)
sudo make install
cd src
rm -r testing
git clone $GIT/google/googletest testing
cd tools
sed -i 's/minidump_upload.m/minidump_upload.cc/' linux/tools_linux.gypi
../../../gyp/gyp --depth=. --generator-output=.. -Goutput_dir=../out tools.gyp --format=cmake
cd ../../out/Default
cmake .
make -j$(nproc) dump_syms
mv dump_syms $BreakpadCache/
cd ..
rm -rf gyp breakpad
- name: Breakpad install.
run: |
cd $LibrariesPath
sudo cp -R breakpad-cache/. /usr/local/
mkdir -p breakpad/out/Default/
cp breakpad-cache/dump_syms breakpad/out/Default/dump_syms
- name: Telegram Desktop build.
if: env.ONLY_CACHE == 'false'
run: |
cd $REPO_NAME/Telegram
DEFINE=""
if [ -n "${{ matrix.defines }}" ]; then
DEFINE="-D ${{ matrix.defines }}=ON"
echo Define from matrix: $DEFINE
fi
./configure.sh -D TDESKTOP_API_TEST=ON -D DESKTOP_APP_USE_PACKAGED=OFF $DEFINE
cd ../out/Debug
make -j$(nproc)
strip -s bin/Telegram
- name: Check.
if: env.ONLY_CACHE == 'false'
run: |
filePath="$REPO_NAME/out/Debug/bin/Telegram"
if test -f "$filePath"; then
echo "Build successfully done! :)"
size=$(stat -c %s "$filePath")
echo "File size of ${filePath}: ${size} Bytes."
else
echo "Build error, output file does not exist."
exit 1
fi
- name: Move artifact.
if: env.UPLOAD_ARTIFACT == 'true'
run: |
cd $REPO_NAME/out/Debug/bin
mkdir artifact
mv Telegram artifact/
- uses: actions/upload-artifact@master
if: env.UPLOAD_ARTIFACT == 'true'
name: Upload artifact.
with:
name: Telegram
path: ${{ env.REPO_NAME }}/out/Debug/bin/artifact/

View File

@@ -32,8 +32,11 @@ jobs:
QT_PREFIX: "/usr/local/desktop-app/Qt-5.12.5"
LIBICONV_VER: "libiconv-1.15"
UPLOAD_ARTIFACT: "false"
MANUAL_CACHING: "1"
ONLY_CACHE: "false"
MANUAL_CACHING: "2"
DOC_PATH: "docs/building-xcode.md"
AUTO_CACHING: "1"
steps:
- name: Get repository name.
run: echo ::set-env name=REPO_NAME::${GITHUB_REPOSITORY##*/}
@@ -60,6 +63,10 @@ jobs:
echo $MIN_MAC >> CACHE_KEY.txt
echo $PREFIX >> CACHE_KEY.txt
echo $MANUAL_CACHING >> CACHE_KEY.txt
if [ "$AUTO_CACHING" == "1" ]; then
thisFile=$REPO_NAME/.github/workflows/mac.yml
echo `md5 -q $thisFile` >> CACHE_KEY.txt
fi
echo ::set-env name=CACHE_KEY::`md5 -q CACHE_KEY.txt`
echo ::add-path::$PWD/Libraries/depot_tools
@@ -117,8 +124,8 @@ jobs:
run: |
cd $LibrariesPath
git clone $GIT/openssl/openssl openssl_$OPENSSL_VER
cd openssl_$OPENSSL_VER
git clone $GIT/openssl/openssl openssl
cd openssl
git checkout OpenSSL_"$OPENSSL_VER"-stable
./Configure \
--prefix=$PREFIX \
@@ -127,6 +134,15 @@ jobs:
$MIN_MAC
make build_libs -j$(nproc)
SSL_DIR=$LibrariesPath/openssl_${{ env.OPENSSL_VER }}
mkdir -p $SSL_DIR/include
copyLib() {
cp $1.a $SSL_DIR/$1.a
}
copyLib libssl
copyLib libcrypto
sudo cp -R include/. $SSL_DIR/include/
- name: Opus cache.
id: cache-opus
uses: actions/cache@v1
@@ -174,7 +190,7 @@ jobs:
id: cache-ffmpeg
uses: actions/cache@v1
with:
path: ${{ env.LibrariesPath }}/ffmpeg
path: ${{ env.LibrariesPath }}/ffmpeg-cache
key: ${{ runner.OS }}-ffmpeg-${{ env.CACHE_KEY }}
- name: FFmpeg.
if: steps.cache-ffmpeg.outputs.cache-hit != 'true'
@@ -188,7 +204,7 @@ jobs:
LDFLAGS=`freetype-config --libs`
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/X11/lib/pkgconfig
./configure --prefix=/usr/local \
./configure --prefix=$LibrariesPath/ffmpeg-cache \
--extra-cflags="$MIN_MAC $UNGUARDED" \
--extra-cxxflags="$MIN_MAC $UNGUARDED" \
--extra-ldflags="$MIN_MAC" \
@@ -290,10 +306,23 @@ jobs:
--enable-muxer=opus
make -j$(nproc)
sudo make install
- name: FFmpeg install.
run: |
cd $LibrariesPath/ffmpeg
sudo make install
cd $LibrariesPath
#List of files from cmake/external/ffmpeg/CMakeLists.txt.
copyLib() {
mkdir -p ffmpeg/$1
\cp -fR ffmpeg-cache/lib/$1.a ffmpeg/$1/$1.a
}
copyLib libavformat
copyLib libavcodec
copyLib libswresample
copyLib libswscale
copyLib libavutil
sudo cp -R ffmpeg-cache/. /usr/local/
sudo cp -R ffmpeg-cache/include/. ffmpeg/
- name: OpenAL Soft.
run: |
@@ -364,8 +393,8 @@ jobs:
cd $LibrariesPath
mv qt-cache Qt-5.12.5
sudo mkdir -p $QT_PREFIX
sudo mv -f Qt-5.12.5 /usr/local/desktop-app/
- name: Build Qt 5.12.5.
sudo mv -f Qt-5.12.5 "$(dirname "$QT_PREFIX")"/
- name: Qt 5.12.5 build.
if: steps.cache-qt.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
@@ -400,7 +429,8 @@ jobs:
make clean
cp -r $QT_PREFIX $LibrariesPath/qt-cache
- name: Build Telegram Desktop.
- name: Telegram Desktop build.
if: env.ONLY_CACHE == 'false'
run: |
cd $REPO_NAME/Telegram
@@ -429,4 +459,4 @@ jobs:
name: Upload artifact.
with:
name: Telegram
path: $REPO_NAME\out\Debug\artifact\
path: ${{ env.REPO_NAME }}/out/Debug/artifact/

99
.github/workflows/snap.yml vendored Normal file
View File

@@ -0,0 +1,99 @@
name: Snap.
on:
push:
paths-ignore:
- 'docs/**'
- '*.md'
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
jobs:
linux:
name: Ubuntu 18.04
runs-on: ubuntu-18.04
env:
UPLOAD_ARTIFACT: "false"
ONLY_CACHE: "false"
MANUAL_CACHING: "3"
steps:
- name: Clone.
uses: actions/checkout@v1
with:
submodules: recursive
- name: First set up.
run: |
sudo apt-get update
sudo apt-get install gcc-8 g++-8 -y
sudo snap install --classic snapcraft
# Workaround for snapcraft
# See https://forum.snapcraft.io/t/permissions-problem-using-snapcraft-in-azure-pipelines/13258
sudo chown root:root /
snapcraft --version > CACHE_KEY.txt
gcc-8 --version >> CACHE_KEY.txt
echo $MANUAL_CACHING >> CACHE_KEY.txt
md5cache=$(md5sum CACHE_KEY.txt | cut -c -32)
echo ::set-env name=CACHE_KEY::$md5cache
awk -v RS="" -v ORS="\n\n" '/^ cmake:/' snap/snapcraft.yaml > CMAKE_CACHE_KEY.txt
md5cache=$(md5sum CMAKE_CACHE_KEY.txt | cut -c -32)
echo ::set-env name=CMAKE_CACHE_KEY::$md5cache
awk -v RS="" -v ORS="\n\n" '/^ enchant:/' snap/snapcraft.yaml > ENCHANT_CACHE_KEY.txt
md5cache=$(md5sum ENCHANT_CACHE_KEY.txt | cut -c -32)
echo ::set-env name=ENCHANT_CACHE_KEY::$md5cache
- name: CMake cache.
id: cache-cmake
uses: actions/cache@v1
with:
path: parts/cmake
key: ${{ runner.OS }}-cmake-${{ env.CACHE_KEY }}-${{ env.CMAKE_CACHE_KEY }}
- name: CMake build.
if: steps.cache-cmake.outputs.cache-hit != 'true'
run: snapcraft build --destructive-mode cmake
- name: Enchant cache.
id: cache-enchant
uses: actions/cache@v1
with:
path: parts/enchant
key: ${{ runner.OS }}-enchant-${{ env.CACHE_KEY }}-${{ env.ENCHANT_CACHE_KEY }}
- name: Enchant build.
if: steps.cache-enchant.outputs.cache-hit != 'true'
run: snapcraft build --destructive-mode enchant
- name: Telegram Desktop snap build.
if: env.ONLY_CACHE == 'false'
run: snapcraft --destructive-mode
- name: Move artifact.
if: env.UPLOAD_ARTIFACT == 'true'
run: |
artifact_name=$(echo telegram-desktop_*.snap)
echo ::set-env name=ARTIFACT_NAME::$artifact_name
mkdir artifact
mv $artifact_name artifact
- uses: actions/upload-artifact@master
if: env.UPLOAD_ARTIFACT == 'true'
name: Upload artifact.
with:
name: ${{ env.ARTIFACT_NAME }}
path: artifact
- name: Remove unneeded directories for cache.
run: |
rm -rf parts/{cmake,enchant}/{build,src,ubuntu}
rm -rf parts/{cmake,enchant}/state/{stage,prime}

View File

@@ -27,8 +27,11 @@ jobs:
QT: "5_12_5"
OPENSSL_VER: "1_1_1"
UPLOAD_ARTIFACT: "false"
MANUAL_CACHING: "1"
ONLY_CACHE: "false"
MANUAL_CACHING: "2"
DOC_PATH: "docs/building-msvc.md"
AUTO_CACHING: "1"
steps:
- name: Get repository name.
shell: bash
@@ -68,6 +71,10 @@ jobs:
- name: Generate cache key.
shell: bash
run: |
if [ "$AUTO_CACHING" == "1" ]; then
thisFile=$REPO_NAME/.github/workflows/win.yml
echo `md5sum $thisFile | awk '{ print $1 }'` >> CACHE_KEY.txt
fi
echo ::set-env name=CACHE_KEY::`md5sum CACHE_KEY.txt | awk '{ print $1 }'`
- name: Choco installs.
@@ -134,6 +141,7 @@ jobs:
move ossl_static.pdb out32
rmdir /S /Q test
rmdir /S /Q .git
- name: Zlib.
shell: cmd
@@ -226,6 +234,7 @@ jobs:
path: ${{ env.LibrariesPath }}/opus
key: ${{ runner.OS }}-opus-${{ env.CACHE_KEY }}
- name: Opus.
if: steps.cache-opus.outputs.cache-hit != 'true'
shell: cmd
run: |
%VC%
@@ -242,7 +251,7 @@ jobs:
uses: actions/cache@v1
with:
path: ${{ env.LibrariesPath }}/ffmpeg
key: ${{ runner.OS }}-ffmpeg-${{ env.CACHE_KEY }}-${{ hashFiles('**/build_ffmpeg_win.sh') }}
key: ${{ runner.OS }}-ffmpeg-${{ env.CACHE_KEY }}-2-${{ hashFiles('**/build_ffmpeg_win.sh') }}
- name: FFmpeg.
if: steps.cache-ffmpeg.outputs.cache-hit != 'true'
shell: cmd
@@ -257,6 +266,8 @@ jobs:
set MSYS2_PATH_TYPE=inherit
call c:\tools\msys64\usr\bin\bash --login ../../%REPO_NAME%/Telegram/Patches/build_ffmpeg_win.sh
rmdir /S /Q .git
- name: Qt 5.12.5 cache.
id: cache-qt
uses: actions/cache@v1
@@ -298,7 +309,7 @@ jobs:
-nomake examples ^
-nomake tests ^
-platform win32-msvc
- name: Build Qt 5.12.5.
- name: Qt 5.12.5 build.
if: steps.cache-qt.outputs.cache-hit != 'true'
shell: cmd
run: |
@@ -321,7 +332,8 @@ jobs:
fi
echo "::set-env name=TDESKTOP_BUILD_DEFINE::$DEFINE"
- name: Build Telegram Desktop.
- name: Telegram Desktop build.
if: env.ONLY_CACHE == 'false'
shell: cmd
run: |
cd %REPO_NAME%\Telegram
@@ -348,4 +360,4 @@ jobs:
if: env.UPLOAD_ARTIFACT == 'true'
with:
name: Telegram
path: ${{ env.REPO_NAME }}\out\Debug\artifact\
path: ${{ env.REPO_NAME }}\out\Debug\artifact\

6
.gitmodules vendored
View File

@@ -61,3 +61,9 @@
[submodule "Telegram/lib_qr"]
path = Telegram/lib_qr
url = https://github.com/desktop-app/lib_qr.git
[submodule "Telegram/ThirdParty/libdbusmenu-qt"]
path = Telegram/ThirdParty/libdbusmenu-qt
url = https://github.com/desktop-app/libdbusmenu-qt.git
[submodule "Telegram/ThirdParty/hunspell"]
path = Telegram/ThirdParty/hunspell
url = https://github.com/hunspell/hunspell

View File

@@ -1,67 +0,0 @@
sudo: required
dist: trusty
language: cpp
cache:
directories:
- $HOME/travisCacheDir
env:
matrix:
- BUILD_VERSION=""
- BUILD_VERSION="disable_register_custom_scheme"
- BUILD_VERSION="disable_crash_reports"
- BUILD_VERSION="disable_network_proxy"
- BUILD_VERSION="disable_desktop_file_generation"
- BUILD_VERSION="disable_gtk_integration"
matrix:
fast_finish: true
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- bison
- build-essential
- cmake
- devscripts
- dpatch
- equivs
- fakeroot
- g++-8
- gcc-8
- git
- gnome-common
- gobject-introspection
- gtk-doc-tools
- libappindicator-dev
- libasound2-dev
- libdbusmenu-glib-dev
- liblzma-dev
- libopus-dev
- libpulse-dev
- libenchant-dev
- libssl-dev
- libdee-dev
- libva-dev
- libvdpau-dev
- libxcb-xkb-dev
- libxkbcommon-dev
- libatspi2.0-dev
- lintian
- quilt
- valac
- xutils-dev
- yasm
before_install:
- export CXX="g++-8" CC="gcc-8"
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 60 --slave /usr/bin/g++ g++ /usr/bin/g++-8
- sudo update-alternatives --config gcc
- g++ --version
script:
- .travis/build.sh

View File

@@ -1,725 +0,0 @@
#!/bin/bash
set -e
REPO="$PWD"
BUILD="$REPO/build"
UPSTREAM="$REPO/upstream"
EXTERNAL="$REPO/external"
CACHE="$HOME/travisCacheDir"
QT_WAS_BUILT="0"
QT_VERSION=5.12.5
XKB_PATH="$BUILD/libxkbcommon"
XKB_CACHE_VERSION="3"
QT_PATH="$BUILD/qt"
QT_CACHE_VERSION="5"
QT_PATCH="$EXTERNAL/patches/qtbase_${QT_VERSION//\./_}.diff"
BREAKPAD_PATH="$BUILD/breakpad"
BREAKPAD_CACHE_VERSION="3"
GYP_PATH="$BUILD/gyp"
GYP_CACHE_VERSION="3"
GYP_PATCH="$EXTERNAL/patches/gyp.diff"
RANGE_PATH="$BUILD/range-v3"
RANGE_CACHE_VERSION="3"
VA_PATH="$BUILD/libva"
VA_CACHE_VERSION="3"
VDPAU_PATH="$BUILD/libvdpau"
VDPAU_CACHE_VERSION="3"
FFMPEG_PATH="$BUILD/ffmpeg"
FFMPEG_CACHE_VERSION="3"
OPENAL_PATH="$BUILD/openal-soft"
OPENAL_CACHE_VERSION="4"
GYP_DEFINES=""
[[ ! $MAKE_ARGS ]] && MAKE_ARGS="--silent -j4"
run() {
# Move files to subdir
cd ..
mv tdesktop tdesktop2
mkdir tdesktop
mv tdesktop2 "$UPSTREAM"
mkdir "$BUILD"
build
check
}
build() {
mkdir -p "$EXTERNAL"
BUILD_VERSION_DATA=$(echo $BUILD_VERSION | cut -d'-' -f 1)
getPatches
# libxkbcommon
getXkbCommon
# libva
getVa
# libvdpau
getVdpau
# ffmpeg
getFFmpeg
# openal_soft
getOpenAL
# Patched Qt
getCustomQt
# Breakpad
getBreakpad
# Patched GYP (supports cmake precompiled headers)
getGYP
# Range v3
getRange
# Guideline Support Library
getGSL
if [ "$QT_WAS_BUILT" == "1" ]; then
error_msg "Qt was built, please restart the job :("
exit 1
fi
# Configure the build
if [[ $BUILD_VERSION == *"disable_register_custom_scheme"* ]]; then
GYP_DEFINES+=",TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME"
fi
if [[ $BUILD_VERSION == *"disable_crash_reports"* ]]; then
GYP_DEFINES+=",DESKTOP_APP_DISABLE_CRASH_REPORTS"
fi
if [[ $BUILD_VERSION == *"disable_network_proxy"* ]]; then
GYP_DEFINES+=",TDESKTOP_DISABLE_NETWORK_PROXY"
fi
if [[ $BUILD_VERSION == *"disable_desktop_file_generation"* ]]; then
GYP_DEFINES+=",TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION"
fi
if [[ $BUILD_VERSION == *"disable_gtk_integration"* ]]; then
GYP_DEFINES+=",TDESKTOP_DISABLE_GTK_INTEGRATION"
fi
info_msg "Build defines: ${GYP_DEFINES}"
buildTelegram
travisEndFold
}
getPatches() {
cd "$EXTERNAL"
git clone --depth 1 https://github.com/desktop-app/patches
}
getXkbCommon() {
travisStartFold "Getting xkbcommon"
local XKB_CACHE="$CACHE/libxkbcommon"
local XKB_CACHE_FILE="$XKB_CACHE/.cache.txt"
local XKB_CACHE_KEY="${XKB_CACHE_VERSION}"
local XKB_CACHE_OUTDATED="1"
if [ ! -d "$XKB_CACHE" ]; then
mkdir -p "$XKB_CACHE"
fi
ln -sf "$XKB_CACHE" "$XKB_PATH"
if [ -f "$XKB_CACHE_FILE" ]; then
local XKB_CACHE_KEY_FOUND=`tail -n 1 $XKB_CACHE_FILE`
if [ "$XKB_CACHE_KEY" == "$XKB_CACHE_KEY_FOUND" ]; then
XKB_CACHE_OUTDATED="0"
else
info_msg "Cache key '$XKB_CACHE_KEY_FOUND' does not match '$XKB_CACHE_KEY', rebuilding libxkbcommon"
fi
fi
if [ "$XKB_CACHE_OUTDATED" == "1" ]; then
buildXkbCommon
sudo echo $XKB_CACHE_KEY > "$XKB_CACHE_FILE"
else
info_msg "Using cached libxkbcommon"
fi
}
buildXkbCommon() {
info_msg "Downloading and building libxkbcommon"
if [ -d "$EXTERNAL/libxkbcommon" ]; then
rm -rf "$EXTERNAL/libxkbcommon"
fi
cd $XKB_PATH
rm -rf *
cd "$EXTERNAL"
git clone https://github.com/xkbcommon/libxkbcommon.git
cd "$EXTERNAL/libxkbcommon"
git checkout xkbcommon-0.8.4
./autogen.sh --prefix=$XKB_PATH
make $MAKE_ARGS
sudo make install
sudo ldconfig
}
getRange() {
travisStartFold "Getting range-v3"
local RANGE_CACHE="$CACHE/range-v3"
local RANGE_CACHE_FILE="$RANGE_CACHE/.cache.txt"
local RANGE_CACHE_KEY="${RANGE_CACHE_VERSION}"
local RANGE_CACHE_OUTDATED="1"
if [ ! -d "$RANGE_CACHE" ]; then
mkdir -p "$RANGE_CACHE"
fi
ln -sf "$RANGE_CACHE" "$RANGE_PATH"
if [ -f "$RANGE_CACHE_FILE" ]; then
local RANGE_CACHE_KEY_FOUND=`tail -n 1 $RANGE_CACHE_FILE`
if [ "$RANGE_CACHE_KEY" == "$RANGE_CACHE_KEY_FOUND" ]; then
RANGE_CACHE_OUTDATED="0"
else
info_msg "Cache key '$RANGE_CACHE_KEY_FOUND' does not match '$RANGE_CACHE_KEY', getting range-v3"
fi
fi
if [ "$RANGE_CACHE_OUTDATED" == "1" ]; then
buildRange
sudo echo $RANGE_CACHE_KEY > "$RANGE_CACHE_FILE"
else
info_msg "Using cached range-v3"
fi
}
buildRange() {
info_msg "Downloading range-v3"
if [ -d "$EXTERNAL/range-v3" ]; then
rm -rf "$EXTERNAL/range-v3"
fi
cd $RANGE_PATH
rm -rf *
cd "$EXTERNAL"
git clone --depth 1 --branch 0.9.1 https://github.com/ericniebler/range-v3
cd "$EXTERNAL/range-v3"
cp -r * "$RANGE_PATH/"
}
getVa() {
travisStartFold "Getting libva"
local VA_CACHE="$CACHE/libva"
local VA_CACHE_FILE="$VA_CACHE/.cache.txt"
local VA_CACHE_KEY="${VA_CACHE_VERSION}"
local VA_CACHE_OUTDATED="1"
if [ ! -d "$VA_CACHE" ]; then
mkdir -p "$VA_CACHE"
fi
ln -sf "$VA_CACHE" "$VA_PATH"
if [ -f "$VA_CACHE_FILE" ]; then
local VA_CACHE_KEY_FOUND=`tail -n 1 $VA_CACHE_FILE`
if [ "$VA_CACHE_KEY" == "$VA_CACHE_KEY_FOUND" ]; then
VA_CACHE_OUTDATED="0"
else
info_msg "Cache key '$VA_CACHE_KEY_FOUND' does not match '$VA_CACHE_KEY', rebuilding libva"
fi
fi
if [ "$VA_CACHE_OUTDATED" == "1" ]; then
buildVa
sudo echo $VA_CACHE_KEY > "$VA_CACHE_FILE"
else
info_msg "Using cached libva"
fi
}
buildVa() {
info_msg "Downloading and building libva"
if [ -d "$EXTERNAL/libva" ]; then
rm -rf "$EXTERNAL/libva"
fi
cd $VA_PATH
rm -rf *
cd "$EXTERNAL"
git clone https://github.com/01org/libva
cd "$EXTERNAL/libva"
./autogen.sh --prefix=$VA_PATH --enable-static
make $MAKE_ARGS
sudo make install
sudo ldconfig
}
getVdpau() {
travisStartFold "Getting libvdpau"
local VDPAU_CACHE="$CACHE/libvdpau"
local VDPAU_CACHE_FILE="$VDPAU_CACHE/.cache.txt"
local VDPAU_CACHE_KEY="${VDPAU_CACHE_VERSION}"
local VDPAU_CACHE_OUTDATED="1"
if [ ! -d "$VDPAU_CACHE" ]; then
mkdir -p "$VDPAU_CACHE"
fi
ln -sf "$VDPAU_CACHE" "$VDPAU_PATH"
if [ -f "$VDPAU_CACHE_FILE" ]; then
local VDPAU_CACHE_KEY_FOUND=`tail -n 1 $VDPAU_CACHE_FILE`
if [ "$VDPAU_CACHE_KEY" == "$VDPAU_CACHE_KEY_FOUND" ]; then
VDPAU_CACHE_OUTDATED="0"
else
info_msg "Cache key '$VDPAU_CACHE_KEY_FOUND' does not match '$VDPAU_CACHE_KEY', rebuilding libvdpau"
fi
fi
if [ "$VDPAU_CACHE_OUTDATED" == "1" ]; then
buildVdpau
sudo echo $VDPAU_CACHE_KEY > "$VDPAU_CACHE_FILE"
else
info_msg "Using cached libvdpau"
fi
}
buildVdpau() {
info_msg "Downloading and building libvdpau"
if [ -d "$EXTERNAL/libvdpau" ]; then
rm -rf "$EXTERNAL/libvdpau"
fi
cd $VDPAU_PATH
rm -rf *
cd "$EXTERNAL"
git clone git://anongit.freedesktop.org/vdpau/libvdpau
cd "$EXTERNAL/libvdpau"
git checkout libvdpau-1.2
./autogen.sh --prefix=$VDPAU_PATH --enable-static
make $MAKE_ARGS
sudo make install
sudo ldconfig
}
getFFmpeg() {
travisStartFold "Getting ffmpeg"
local FFMPEG_CACHE="$CACHE/ffmpeg"
local FFMPEG_CACHE_FILE="$FFMPEG_CACHE/.cache.txt"
local FFMPEG_CACHE_KEY="${FFMPEG_CACHE_VERSION}"
local FFMPEG_CACHE_OUTDATED="1"
if [ ! -d "$FFMPEG_CACHE" ]; then
mkdir -p "$FFMPEG_CACHE"
fi
ln -sf "$FFMPEG_CACHE" "$FFMPEG_PATH"
if [ -f "$FFMPEG_CACHE_FILE" ]; then
local FFMPEG_CACHE_KEY_FOUND=`tail -n 1 $FFMPEG_CACHE_FILE`
if [ "$FFMPEG_CACHE_KEY" == "$FFMPEG_CACHE_KEY_FOUND" ]; then
FFMPEG_CACHE_OUTDATED="0"
else
info_msg "Cache key '$FFMPEG_CACHE_KEY_FOUND' does not match '$FFMPEG_CACHE_KEY', rebuilding ffmpeg"
fi
fi
if [ "$FFMPEG_CACHE_OUTDATED" == "1" ]; then
buildFFmpeg
sudo echo $FFMPEG_CACHE_KEY > "$FFMPEG_CACHE_FILE"
else
info_msg "Using cached ffmpeg"
fi
}
buildFFmpeg() {
info_msg "Downloading and building ffmpeg"
if [ -d "$EXTERNAL/ffmpeg" ]; then
rm -rf "$EXTERNAL/ffmpeg"
fi
cd $FFMPEG_PATH
rm -rf *
cd "$EXTERNAL"
git clone https://git.ffmpeg.org/ffmpeg.git
cd "$EXTERNAL/ffmpeg"
git checkout release/3.4
./configure \
--prefix=$FFMPEG_PATH \
--disable-debug \
--disable-programs \
--disable-doc \
--disable-everything \
--enable-gpl \
--enable-version3 \
--enable-libopus \
--enable-decoder=aac \
--enable-decoder=aac_latm \
--enable-decoder=aasc \
--enable-decoder=flac \
--enable-decoder=gif \
--enable-decoder=h264 \
--enable-decoder=h264_vdpau \
--enable-decoder=mp1 \
--enable-decoder=mp1float \
--enable-decoder=mp2 \
--enable-decoder=mp2float \
--enable-decoder=mp3 \
--enable-decoder=mp3adu \
--enable-decoder=mp3adufloat \
--enable-decoder=mp3float \
--enable-decoder=mp3on4 \
--enable-decoder=mp3on4float \
--enable-decoder=mpeg4 \
--enable-decoder=mpeg4_vdpau \
--enable-decoder=msmpeg4v2 \
--enable-decoder=msmpeg4v3 \
--enable-decoder=opus \
--enable-decoder=vorbis \
--enable-decoder=wavpack \
--enable-decoder=wmalossless \
--enable-decoder=wmapro \
--enable-decoder=wmav1 \
--enable-decoder=wmav2 \
--enable-decoder=wmavoice \
--enable-encoder=libopus \
--enable-hwaccel=h264_vaapi \
--enable-hwaccel=h264_vdpau \
--enable-hwaccel=mpeg4_vaapi \
--enable-hwaccel=mpeg4_vdpau \
--enable-parser=aac \
--enable-parser=aac_latm \
--enable-parser=flac \
--enable-parser=h264 \
--enable-parser=mpeg4video \
--enable-parser=mpegaudio \
--enable-parser=opus \
--enable-parser=vorbis \
--enable-demuxer=aac \
--enable-demuxer=flac \
--enable-demuxer=gif \
--enable-demuxer=h264 \
--enable-demuxer=mov \
--enable-demuxer=mp3 \
--enable-demuxer=ogg \
--enable-demuxer=wav \
--enable-muxer=ogg \
--enable-muxer=opus
make $MAKE_ARGS
sudo make install
sudo ldconfig
}
getOpenAL() {
travisStartFold "Getting openal-soft"
local OPENAL_CACHE="$CACHE/openal-soft"
local OPENAL_CACHE_FILE="$OPENAL_CACHE/.cache.txt"
local OPENAL_CACHE_KEY="${OPENAL_CACHE_VERSION}"
local OPENAL_CACHE_OUTDATED="1"
if [ ! -d "$OPENAL_CACHE" ]; then
mkdir -p "$OPENAL_CACHE"
fi
ln -sf "$OPENAL_CACHE" "$OPENAL_PATH"
if [ -f "$OPENAL_CACHE_FILE" ]; then
local OPENAL_CACHE_KEY_FOUND=`tail -n 1 $OPENAL_CACHE_FILE`
if [ "$OPENAL_CACHE_KEY" == "$OPENAL_CACHE_KEY_FOUND" ]; then
OPENAL_CACHE_OUTDATED="0"
else
info_msg "Cache key '$OPENAL_CACHE_KEY_FOUND' does not match '$OPENAL_CACHE_KEY', rebuilding openal-soft"
fi
fi
if [ "$OPENAL_CACHE_OUTDATED" == "1" ]; then
buildOpenAL
sudo echo $OPENAL_CACHE_KEY > "$OPENAL_CACHE_FILE"
else
info_msg "Using cached openal-soft"
fi
}
buildOpenAL() {
info_msg "Downloading and building openal-soft"
if [ -d "$EXTERNAL/openal-soft" ]; then
rm -rf "$EXTERNAL/openal-soft"
fi
cd $OPENAL_PATH
sudo rm -rf *
cd "$EXTERNAL"
git clone https://github.com/kcat/openal-soft.git
cd openal-soft
git checkout openal-soft-1.19.1
cd "$EXTERNAL/openal-soft/build"
cmake \
-D CMAKE_INSTALL_PREFIX=$OPENAL_PATH \
-D CMAKE_BUILD_TYPE=Release \
-D LIBTYPE=STATIC \
-D ALSOFT_EXAMPLES=OFF \
-D ALSOFT_TESTS=OFF \
-D ALSOFT_UTILS=OFF \
..
make $MAKE_ARGS
sudo make install
sudo ldconfig
}
getBreakpad() {
travisStartFold "Getting breakpad"
local BREAKPAD_CACHE="$CACHE/breakpad"
local BREAKPAD_CACHE_FILE="$BREAKPAD_CACHE/.cache.txt"
local BREAKPAD_CACHE_KEY="${BREAKPAD_CACHE_VERSION}"
local BREAKPAD_CACHE_OUTDATED="1"
if [ ! -d "$BREAKPAD_CACHE" ]; then
mkdir -p "$BREAKPAD_CACHE"
fi
ln -sf "$BREAKPAD_CACHE" "$BREAKPAD_PATH"
if [ -f "$BREAKPAD_CACHE_FILE" ]; then
local BREAKPAD_CACHE_KEY_FOUND=`tail -n 1 $BREAKPAD_CACHE_FILE`
if [ "$BREAKPAD_CACHE_KEY" == "$BREAKPAD_CACHE_KEY_FOUND" ]; then
BREAKPAD_CACHE_OUTDATED="0"
else
info_msg "Cache key '$BREAKPAD_CACHE_KEY_FOUND' does not match '$BREAKPAD_CACHE_KEY', rebuilding breakpad"
fi
fi
if [ "$BREAKPAD_CACHE_OUTDATED" == "1" ]; then
buildBreakpad
sudo echo $BREAKPAD_CACHE_KEY > "$BREAKPAD_CACHE_FILE"
else
info_msg "Using cached breakpad"
fi
}
buildBreakpad() {
info_msg "Downloading and building breakpad"
if [ -d "$EXTERNAL/breakpad" ]; then
rm -rf "$EXTERNAL/breakpad"
fi
cd $BREAKPAD_PATH
rm -rf *
cd "$EXTERNAL"
git clone https://chromium.googlesource.com/breakpad/breakpad
cd "$EXTERNAL/breakpad/src/third_party"
git clone https://chromium.googlesource.com/linux-syscall-support lss
cd "$EXTERNAL/breakpad"
./configure --prefix=$BREAKPAD_PATH
make $MAKE_ARGS
sudo make install
sudo ldconfig
}
getCustomQt() {
travisStartFold "Getting patched Qt"
local QT_CACHE="$CACHE/qtPatched"
local QT_CACHE_FILE="$QT_CACHE/.cache.txt"
local QT_PATCH_CHECKSUM=`sha1sum $QT_PATCH`
local QT_CACHE_KEY="${QT_VERSION}_${QT_CACHE_VERSION}_${QT_PATCH_CHECKSUM:0:32}"
local QT_CACHE_OUTDATED="1"
if [ ! -d "$QT_CACHE" ]; then
mkdir -p "$QT_CACHE"
fi
ln -sf "$QT_CACHE" "$QT_PATH"
if [ -f "$QT_CACHE_FILE" ]; then
local QT_CACHE_KEY_FOUND=`tail -n 1 $QT_CACHE_FILE`
if [ "$QT_CACHE_KEY" == "$QT_CACHE_KEY_FOUND" ]; then
QT_CACHE_OUTDATED="0"
else
info_msg "Cache key '$QT_CACHE_KEY_FOUND' does not match '$QT_CACHE_KEY', rebuilding patched Qt"
fi
fi
if [ "$QT_CACHE_OUTDATED" == "1" ]; then
buildCustomQt
sudo echo $QT_CACHE_KEY > "$QT_CACHE_FILE"
else
info_msg "Using cached patched Qt"
fi
export PATH="$QT_PATH/bin:$PATH"
}
buildCustomQt() {
QT_WAS_BUILT="1"
info_msg "Downloading and building patched qt"
if [ -d "$EXTERNAL/qt_${QT_VERSION}" ]; then
sudo rm -rf "$EXTERNAL/qt_${QT_VERSION}"
fi
cd $QT_PATH
sudo rm -rf *
cd "$EXTERNAL"
git clone git://code.qt.io/qt/qt5.git qt${QT_VERSION}
cd "$EXTERNAL/qt${QT_VERSION}"
perl init-repository --branch --module-subset=qtbase,qtimageformats
git checkout v${QT_VERSION}
git submodule update qtbase
git submodule update qtimageformats
cd "$EXTERNAL/qt${QT_VERSION}/qtbase"
git apply "$QT_PATCH"
cd ..
cd "$EXTERNAL/qt${QT_VERSION}/qtbase/src/plugins/platforminputcontexts"
git clone https://github.com/telegramdesktop/fcitx.git
git clone https://github.com/telegramdesktop/hime.git
git clone https://github.com/telegramdesktop/nimf.git
cd ../../../..
./configure -prefix $QT_PATH -release -opensource -confirm-license -qt-zlib \
-qt-libpng -qt-libjpeg -qt-harfbuzz -qt-pcre -qt-xcb \
-system-freetype -fontconfig -no-opengl -no-gtk -static \
-nomake examples -nomake tests -no-mirclient \
-dbus-runtime -no-mtdev # <- Not sure about these
make $MAKE_ARGS
sudo make install
}
getGSL() {
cd "$UPSTREAM"
git submodule init
git submodule update
}
getGYP() {
travisStartFold "Getting patched GYP"
local GYP_CACHE="$CACHE/gyp"
local GYP_CACHE_FILE="$GYP_CACHE/.cache.txt"
local GYP_PATCH_CHECKSUM=`sha1sum $GYP_PATCH`
local GYP_CACHE_KEY="${GYP_CACHE_VERSION}_${GYP_PATCH_CHECKSUM:0:32}"
local GYP_CACHE_OUTDATED="1"
if [ ! -d "$GYP_CACHE" ]; then
mkdir -p "$GYP_CACHE"
fi
ln -sf "$GYP_CACHE" "$GYP_PATH"
if [ -f "$GYP_CACHE_FILE" ]; then
local GYP_CACHE_KEY_FOUND=`tail -n 1 $GYP_CACHE_FILE`
if [ "$GYP_CACHE_KEY" == "$GYP_CACHE_KEY_FOUND" ]; then
GYP_CACHE_OUTDATED="0"
else
info_msg "Cache key '$GYP_CACHE_KEY_FOUND' does not match '$GYP_CACHE_KEY', rebuilding patched GYP"
fi
fi
if [ "$GYP_CACHE_OUTDATED" == "1" ]; then
buildGYP
sudo echo $GYP_CACHE_KEY > "$GYP_CACHE_FILE"
else
info_msg "Using cached patched GYP"
fi
}
buildGYP() {
info_msg "Downloading and building patched GYP"
if [ -d "$EXTERNAL/gyp" ]; then
rm -rf "$EXTERNAL/gyp"
fi
cd $GYP_PATH
rm -rf *
cd "$EXTERNAL"
git clone https://chromium.googlesource.com/external/gyp
cd "$EXTERNAL/gyp"
git checkout 9f2a7bb1
git apply "$GYP_PATCH"
cp -r * "$GYP_PATH/"
}
buildTelegram() {
travisStartFold "Build tdesktop"
cd "$UPSTREAM/Telegram/gyp"
"$GYP_PATH/gyp" \
-Dapi_id=17349 \
-Dapi_hash=344583e45741c457fe1862106095a5eb \
-Dspecial_build_target= \
-Dbuild_defines=${GYP_DEFINES:1} \
-Dlinux_path_xkbcommon=$XKB_PATH \
-Dlinux_path_va=$VA_PATH \
-Dlinux_path_vdpau=$VDPAU_PATH \
-Dlinux_path_ffmpeg=$FFMPEG_PATH \
-Dlinux_path_openal=$OPENAL_PATH \
-Dlinux_path_range=$RANGE_PATH \
-Dlinux_path_qt=$QT_PATH \
-Dlinux_path_breakpad=$BREAKPAD_PATH \
-Dlinux_path_libexif_lib=/usr/local/lib \
-Dlinux_lib_ssl=-lssl \
-Dlinux_lib_crypto=-lcrypto \
-Dlinux_lib_icu=-licuuc\ -licutu\ -licui18n \
--depth=. --generator-output=.. --format=cmake -Goutput_dir=../out \
Telegram.gyp
cd "$UPSTREAM/out/Debug"
export ASM="gcc"
cmake .
make $MAKE_ARGS
}
check() {
local filePath="$UPSTREAM/out/Debug/Telegram"
if test -f "$filePath"; then
success_msg "Build successfully done! :)"
local size;
size=$(stat -c %s "$filePath")
success_msg "File size of ${filePath}: ${size} Bytes"
else
error_msg "Build error, output file does not exist"
exit 1
fi
}
source ./.travis/common.sh
run

View File

@@ -1,64 +0,0 @@
#!/usr/bin/env bash
# set colors
RCol='\e[0m' # Text Reset
# Regular Bold Underline High Intensity BoldHigh Intens Background High Intensity Backgrounds
Bla='\e[0;30m'; BBla='\e[1;30m'; UBla='\e[4;30m'; IBla='\e[0;90m'; BIBla='\e[1;90m'; On_Bla='\e[40m'; On_IBla='\e[0;100m';
Red='\e[0;31m'; BRed='\e[1;31m'; URed='\e[4;31m'; IRed='\e[0;91m'; BIRed='\e[1;91m'; On_Red='\e[41m'; On_IRed='\e[0;101m';
Gre='\e[0;32m'; BGre='\e[1;32m'; UGre='\e[4;32m'; IGre='\e[0;92m'; BIGre='\e[1;92m'; On_Gre='\e[42m'; On_IGre='\e[0;102m';
Yel='\e[0;33m'; BYel='\e[1;33m'; UYel='\e[4;33m'; IYel='\e[0;93m'; BIYel='\e[1;93m'; On_Yel='\e[43m'; On_IYel='\e[0;103m';
Blu='\e[0;34m'; BBlu='\e[1;34m'; UBlu='\e[4;34m'; IBlu='\e[0;94m'; BIBlu='\e[1;94m'; On_Blu='\e[44m'; On_IBlu='\e[0;104m';
Pur='\e[0;35m'; BPur='\e[1;35m'; UPur='\e[4;35m'; IPur='\e[0;95m'; BIPur='\e[1;95m'; On_Pur='\e[45m'; On_IPur='\e[0;105m';
Cya='\e[0;36m'; BCya='\e[1;36m'; UCya='\e[4;36m'; ICya='\e[0;96m'; BICya='\e[1;96m'; On_Cya='\e[46m'; On_ICya='\e[0;106m';
Whi='\e[0;37m'; BWhi='\e[1;37m'; UWhi='\e[4;37m'; IWhi='\e[0;97m'; BIWhi='\e[1;97m'; On_Whi='\e[47m'; On_IWhi='\e[0;107m';
start_msg() {
echo -e "\n${Gre}$*${RCol}"
}
info_msg() {
sameLineInfoMessage "\n$1"
}
error_msg() {
echo -e "\n${BRed}$*${RCol}"
}
success_msg() {
echo -e "\n${BGre}$*${RCol}"
}
sameLineInfoMessage() {
echo -e "${Cya}$*${RCol}"
}
TRAVIS_LAST_FOLD=""
travisStartFold() {
local TITLE="$1"
local NAME=$(sanitizeName "$TITLE")
if [ "$TRAVIS_LAST_FOLD" != "" ]; then
travisEndFold
fi
echo "travis_fold:start:$NAME"
sameLineInfoMessage "$TITLE"
TRAVIS_LAST_FOLD="$NAME"
}
travisEndFold() {
if [ "$TRAVIS_LAST_FOLD" == "" ]; then
return
fi
echo "travis_fold:end:$TRAVIS_LAST_FOLD"
TRAVIS_LAST_FOLD=""
}
sanitizeName() {
local NAME="${1// /_}"
local NAME="${NAME,,}"
echo "$NAME"
}

View File

@@ -5,8 +5,7 @@ This is the complete source code and the build instructions for the alpha versio
[![Version](https://badge.fury.io/gh/telegramdesktop%2Ftdesktop.svg)](https://github.com/telegramdesktop/tdesktop/releases)
[![Build Status](https://github.com/telegramdesktop/tdesktop/workflows/Windows./badge.svg)](https://github.com/telegramdesktop/tdesktop/actions)
[![Build Status](https://github.com/telegramdesktop/tdesktop/workflows/MacOS./badge.svg)](https://github.com/telegramdesktop/tdesktop/actions)
[![Build Status](https://travis-ci.org/telegramdesktop/tdesktop.svg?branch=dev)](https://travis-ci.org/telegramdesktop/tdesktop)
[![Build status](https://ci.appveyor.com/api/projects/status/uiw2y768iy4i5bu8/branch/dev?svg=true)](https://ci.appveyor.com/project/telegramdesktop/tdesktop)
[![Build Status](https://github.com/telegramdesktop/tdesktop/workflows/Linux./badge.svg)](https://github.com/telegramdesktop/tdesktop/actions)
[![Preview of Telegram Desktop][preview_image]][preview_image_url]

View File

@@ -20,6 +20,7 @@ add_subdirectory(lib_lottie)
add_subdirectory(lib_qr)
add_subdirectory(codegen)
include(CheckCXXSourceCompiles)
include(lib_ui/cmake/generate_styles.cmake)
include(cmake/generate_lang.cmake)
include(cmake/generate_numbers.cmake)
@@ -42,7 +43,7 @@ set(style_files
history/history.style
info/info.style
intro/intro.style
media/view/mediaview.style
media/view/media_view.style
media/player/media_player.style
overview/overview.style
passport/passport.style
@@ -65,17 +66,18 @@ generate_numbers(Telegram ${res_loc}/numbers.txt)
set_target_properties(Telegram PROPERTIES AUTOMOC ON AUTORCC ON)
if (DESKTOP_APP_USE_PACKAGED)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
find_package(Threads)
if (LINUX AND NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
target_link_libraries(Telegram
PRIVATE
${CMAKE_DL_LIBS}
Threads::Threads
desktop-app::external_statusnotifieritem
desktop-app::external_dbusmenu_qt
)
endif()
if (add_hunspell_library)
target_link_libraries(Telegram PRIVATE desktop-app::external_hunspell)
endif()
target_link_libraries(Telegram
PRIVATE
tdesktop::lib_mtproto
@@ -105,6 +107,27 @@ if (NOT DESKTOP_APP_USE_PACKAGED)
target_link_libraries(Telegram PRIVATE desktop-app::external_opus)
endif()
# Telegram uses long atomic types, so on some architectures libatomic is needed.
check_cxx_source_compiles("
#include <atomic>
std::atomic_int64_t foo;
int main() {return foo;}
" HAVE_LONG_ATOMIC_WITHOUT_LIB)
if (NOT HAVE_LONG_ATOMIC_WITHOUT_LIB)
target_link_libraries(Telegram PRIVATE atomic)
endif()
if (DESKTOP_APP_USE_PACKAGED)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
find_package(Threads)
target_link_libraries(Telegram
PRIVATE
${CMAKE_DL_LIBS}
Threads::Threads
)
endif()
target_precompile_headers(Telegram PRIVATE ${src_loc}/stdafx.h)
nice_target_sources(Telegram ${src_loc}
PRIVATE
@@ -166,6 +189,8 @@ PRIVATE
boxes/connection_box.h
boxes/create_poll_box.cpp
boxes/create_poll_box.h
boxes/dictionaries_manager.cpp
boxes/dictionaries_manager.h
boxes/download_path_box.cpp
boxes/download_path_box.h
boxes/edit_caption_box.cpp
@@ -238,6 +263,8 @@ PRIVATE
chat_helpers/gifs_list_widget.h
chat_helpers/message_field.cpp
chat_helpers/message_field.h
chat_helpers/spellchecker_common.cpp
chat_helpers/spellchecker_common.h
chat_helpers/stickers.cpp
chat_helpers/stickers.h
chat_helpers/stickers_emoji_pack.cpp
@@ -316,8 +343,12 @@ PRIVATE
data/data_game.h
data/data_groups.cpp
data/data_groups.h
data/data_histories.cpp
data/data_histories.h
data/data_location.cpp
data/data_location.h
data/data_media_rotation.cpp
data/data_media_rotation.h
data/data_media_types.cpp
data/data_media_types.h
data/data_messages.cpp
@@ -518,6 +549,10 @@ PRIVATE
info/media/info_media_widget.h
info/members/info_members_widget.cpp
info/members/info_members_widget.h
info/polls/info_polls_results_inner_widget.cpp
info/polls/info_polls_results_inner_widget.h
info/polls/info_polls_results_widget.cpp
info/polls/info_polls_results_widget.h
info/profile/info_profile_actions.cpp
info/profile/info_profile_actions.h
info/profile/info_profile_cover.cpp
@@ -649,14 +684,16 @@ PRIVATE
media/streaming/media_streaming_utility.h
media/streaming/media_streaming_video_track.cpp
media/streaming/media_streaming_video_track.h
media/view/media_view_playback_controls.cpp
media/view/media_view_playback_controls.h
media/view/media_view_playback_progress.cpp
media/view/media_view_playback_progress.h
media/view/media_view_group_thumbs.cpp
media/view/media_view_group_thumbs.h
media/view/media_view_overlay_widget.cpp
media/view/media_view_overlay_widget.h
media/view/media_view_pip.cpp
media/view/media_view_pip.h
media/view/media_view_playback_controls.cpp
media/view/media_view_playback_controls.h
media/view/media_view_playback_progress.cpp
media/view/media_view_playback_progress.h
mtproto/config_loader.cpp
mtproto/config_loader.h
mtproto/connection_abstract.cpp
@@ -819,6 +856,8 @@ PRIVATE
storage/serialize_common.h
storage/serialize_document.cpp
storage/serialize_document.h
storage/storage_cloud_blob.cpp
storage/storage_cloud_blob.h
storage/storage_facade.cpp
storage/storage_facade.h
# storage/storage_feed_messages.cpp
@@ -841,6 +880,8 @@ PRIVATE
support/support_helper.h
support/support_templates.cpp
support/support_templates.h
ui/effects/fireworks_animation.cpp
ui/effects/fireworks_animation.h
ui/effects/round_checkbox.cpp
ui/effects/round_checkbox.h
ui/effects/send_action_animations.cpp
@@ -1042,35 +1083,13 @@ elseif (LINUX)
find_library(X11_LIBRARY X11)
target_link_libraries(Telegram PRIVATE ${X11_LIBRARY})
endif()
set(appindicator_packages
ayatana-appindicator3-0.1
ayatana-appindicator-0.1
appindicator3-0.1
appindicator-0.1
)
set(appindicator_found 0)
foreach (package ${appindicator_packages})
pkg_check_modules(APPIND_${package} ${package})
if (APPIND_${package}_FOUND)
set(appindicator_found 1)
target_include_directories(Telegram PRIVATE "${APPIND_${package}_INCLUDE_DIRS}")
if (${package} MATCHES "ayatana")
target_compile_definitions(Telegram PRIVATE TDESKTOP_USE_AYATANA_INDICATORS)
endif()
break()
endif()
endforeach()
if (NOT ${appindicator_found})
message(FATAL_ERROR "No libappindicator found by pkg-config.")
endif()
endif()
endif()
if (build_macstore)
set(bundle_identifier "org.telegram.desktop")
set(bundle_entitlements "Telegram Desktop.entitlements")
set(output_name "Telegram Desktop")
set(bundle_entitlements "Telegram Lite.entitlements")
set(output_name "Telegram Lite")
set_target_properties(Telegram PROPERTIES
XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${libs_loc}/breakpad/src/client/mac/build/Release
)
@@ -1088,7 +1107,11 @@ elseif (build_osx)
else()
set(bundle_identifier "com.tdesktop.Telegram$<$<CONFIG:Debug>:Debug>")
set(bundle_entitlements "Telegram.entitlements")
set(output_name "Telegram")
if (LINUX AND DESKTOP_APP_USE_PACKAGED)
set(output_name "telegram-desktop")
else()
set(output_name "Telegram")
endif()
endif()
set_target_properties(Telegram PROPERTIES
@@ -1110,7 +1133,7 @@ set_target_properties(Telegram PROPERTIES
)
set(entitlement_sources
"${CMAKE_CURRENT_SOURCE_DIR}/Telegram/Telegram.entitlements"
"${CMAKE_CURRENT_SOURCE_DIR}/Telegram/Telegram Desktop.entitlements"
"${CMAKE_CURRENT_SOURCE_DIR}/Telegram/Telegram Lite.entitlements"
)
target_sources(Telegram PRIVATE ${entitlement_sources})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/Telegram PREFIX Resources FILES ${entitlement_sources})
@@ -1159,7 +1182,7 @@ if ((NOT disable_autoupdate OR NOT LINUX) AND NOT build_macstore AND NOT build_w
set_target_properties(Updater PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder})
if (LINUX)
target_link_options(Updater INTERFACE -static-libstdc++)
target_link_options(Updater PRIVATE -static-libstdc++)
endif()
if (DESKTOP_APP_SPECIAL_TARGET)
@@ -1185,3 +1208,17 @@ if ((NOT disable_autoupdate OR NOT LINUX) AND NOT build_macstore AND NOT build_w
set_target_properties(Packer PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder})
endif()
endif()
if (LINUX AND DESKTOP_APP_USE_PACKAGED)
include(GNUInstallDirs)
install(TARGETS Telegram RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}")
install(FILES "Resources/art/icon16.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME "telegram.png")
install(FILES "Resources/art/icon32.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/32x32/apps" RENAME "telegram.png")
install(FILES "Resources/art/icon48.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps" RENAME "telegram.png")
install(FILES "Resources/art/icon64.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/64x64/apps" RENAME "telegram.png")
install(FILES "Resources/art/icon128.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps" RENAME "telegram.png")
install(FILES "Resources/art/icon256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "telegram.png")
install(FILES "Resources/art/icon512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "telegram.png")
install(FILES "../lib/xdg/telegramdesktop.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.desktop")
install(FILES "../lib/xdg/telegramdesktop.appdata.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.appdata.xml")
endif()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 B

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 417 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 919 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 747 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

View File

@@ -158,6 +158,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_deleted_message" = "Deleted message";
"lng_pinned_message" = "Pinned message";
"lng_pinned_poll" = "Pinned poll";
"lng_pinned_quiz" = "Pinned quiz";
"lng_pinned_unpin_sure" = "Would you like to unpin this message?";
"lng_pinned_pin_sure" = "Would you like to pin this message?";
"lng_pinned_pin" = "Pin";
@@ -421,6 +422,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_spellchecker" = "Spell checker";
"lng_settings_system_spellchecker" = "Use system spell checker";
"lng_settings_custom_spellchecker" = "Use spell checker";
"lng_settings_auto_download_dictionaries" = "Automatic dictionaries download";
"lng_settings_manage_dictionaries" = "Manage dictionaries";
"lng_settings_manage_enabled_dictionary" = "Dictionary is enabled";
"lng_settings_manage_remove_dictionary" = "Remove Dictionary";
"lng_backgrounds_header" = "Choose your new chat background";
"lng_theme_sure_keep" = "Keep this theme?";
@@ -1267,6 +1273,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_cant_invite_make_admin" = "Make admin";
"lng_send_button" = "Send";
"lng_schedule_button" = "Schedule";
"lng_send_silent_message" = "Send without sound";
"lng_schedule_message" = "Schedule message";
"lng_reminder_message" = "Set a reminder";
@@ -1294,6 +1301,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_scheduled_messages" = "Scheduled Messages";
"lng_reminder_messages" = "Reminders";
"lng_scheduled_date" = "Scheduled for {date}";
"lng_scheduled_date_until_online" = "Scheduled until online";
"lng_scheduled_send_until_online" = "Send when online";
"lng_scheduled_send_now" = "Send message now?";
"lng_scheduled_send_now_many#one" = "Send {count} message now?";
"lng_scheduled_send_now_many#other" = "Send {count} messages now?";
@@ -1364,7 +1374,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_user_action_upload_file" = "{user} is sending a file";
"lng_unread_bar#one" = "{count} unread message";
"lng_unread_bar#other" = "{count} unread messages";
//"lng_unread_bar_some" = "Unread messages";
"lng_unread_bar_some" = "Unread messages";
"lng_maps_point" = "Location";
"lng_save_photo" = "Save image";
@@ -1453,6 +1463,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_send_album" = "Send as an album";
"lng_send_photo" = "Send as a photo";
"lng_send_file" = "Send as a file";
"lng_send_media_invalid_files" = "Sorry, no valid files found.";
"lng_forward_choose" = "Choose recipient...";
"lng_forward_cant" = "Sorry, no way to forward here :(";
@@ -1573,6 +1584,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_mediaview_saved_to" = "Image was saved to your {downloads} folder";
"lng_mediaview_downloads" = "Downloads";
"lng_mediaview_video_loading" = "Loading - {percent}";
"lng_mediaview_playback_speed" = "Playback speed";
"lng_mediaview_playback_speed_normal" = "Normal";
"lng_mediaview_rotate_video" = "Rotate video";
"lng_theme_preview_title" = "Theme Preview";
"lng_theme_preview_generating" = "Generating color theme preview...";
@@ -1602,6 +1616,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_formatting_link_url" = "URL";
"lng_formatting_link_create" = "Create";
"lng_spellchecker_submenu" = "Spelling";
"lng_spellchecker_add" = "Add to Dictionary";
"lng_spellchecker_remove" = "Remove from Dictionary";
"lng_spellchecker_ignore" = "Ignore word";
@@ -1797,6 +1812,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_restricted_send_inline_all" = "Posting inline content isn't allowed in this group.";
"lng_restricted_send_polls_all" = "Posting polls isn't allowed in this group.";
"lng_restricted_send_public_polls" = "Sorry, public polls can't be forwarded to channels.";
"lng_exceptions_list_title" = "Exceptions";
"lng_removed_list_title" = "Removed users";
@@ -2166,10 +2183,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_launch_exe_dont_ask" = "Don't ask me again";
"lng_polls_anonymous" = "Anonymous Poll";
"lng_polls_public" = "Poll";
"lng_polls_anonymous_quiz" = "Anonymous Quiz";
"lng_polls_public_quiz" = "Quiz";
"lng_polls_closed" = "Final results";
"lng_polls_votes_count#one" = "{count} vote";
"lng_polls_votes_count#other" = "{count} votes";
"lng_polls_votes_none" = "No votes";
"lng_polls_answers_count#one" = "{count} answer";
"lng_polls_answers_count#other" = "{count} answers";
"lng_polls_answers_none" = "No answers";
"lng_polls_submit_votes" = "Vote";
"lng_polls_view_results" = "View results";
"lng_polls_retract" = "Retract vote";
"lng_polls_stop" = "Stop poll";
"lng_polls_stop_warning" = "If you stop this poll now, nobody will be able to vote in it anymore. This action cannot be undone.";
@@ -2183,7 +2208,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_polls_create_limit#one" = "You can add {count} more option.";
"lng_polls_create_limit#other" = "You can add {count} more options.";
"lng_polls_create_maximum" = "You have added the maximum number of options.";
"lng_polls_create_settings" = "Settings";
"lng_polls_create_anonymous" = "Anonymous Votes";
"lng_polls_create_multiple_choice" = "Multiple Answers";
"lng_polls_create_quiz_mode" = "Quiz Mode";
"lng_polls_create_button" = "Create";
"lng_polls_create_one_answer" = "Quiz has only one right answer.";
"lng_polls_choose_question" = "Please enter a question.";
"lng_polls_choose_answers" = "Please enter at least two options.";
"lng_polls_choose_correct" = "Please choose the correct answer.";
"lng_polls_poll_results_title" = "Poll results";
"lng_polls_quiz_results_title" = "Quiz results";
"lng_polls_show_more#one" = "Show more ({count})";
"lng_polls_show_more#other" = "Show more ({count})";
"lng_polls_votes_collapse" = "Collapse";
"lng_outdated_title" = "PLEASE UPDATE YOUR OPERATING SYSTEM.";
"lng_outdated_soon" = "Otherwise, Telegram Desktop will stop updating on {date}.";

View File

@@ -56,5 +56,6 @@
</qresource>
<qresource prefix="/misc">
<file alias="default_shortcuts-custom.json">../../default_shortcuts-custom.json</file>
<file alias="telegramdesktop.desktop">../../../../lib/xdg/telegramdesktop.desktop</file>
</qresource>
</RCC>

View File

@@ -71,7 +71,7 @@ inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int =
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
inputMediaGeoLive#ce4e82fd flags:# stopped:flags.0?true geo_point:InputGeoPoint period:flags.1?int = InputMedia;
inputMediaPoll#6b3765b poll:Poll = InputMedia;
inputMediaPoll#abe9ca25 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> = InputMedia;
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
@@ -351,6 +351,7 @@ updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector<int> = Update;
updateTheme#8216fba3 theme:Theme = Update;
updateGeoLiveViewed#871fb939 peer:Peer msg_id:int = Update;
updateLoginToken#564fe691 = Update;
updateMessagePollVote#42f88f2c poll_id:long user_id:int options:Vector<bytes> = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@@ -546,6 +547,7 @@ keyboardButtonGame#50f41ccf text:string = KeyboardButton;
keyboardButtonBuy#afd93fbb text:string = KeyboardButton;
keyboardButtonUrlAuth#10b78d29 flags:# text:string fwd_text:flags.0?string url:string button_id:int = KeyboardButton;
inputKeyboardButtonUrlAuth#d02e7fd4 flags:# request_write_access:flags.0?true text:string fwd_text:flags.1?string url:string bot:InputUser = KeyboardButton;
keyboardButtonRequestPoll#bbc7515d flags:# quiz:flags.0?Bool text:string = KeyboardButton;
keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
@@ -1015,11 +1017,11 @@ help.userInfo#1eb3758 message:string entities:Vector<MessageEntity> author:strin
pollAnswer#6ca9c2e9 text:string option:bytes = PollAnswer;
poll#d5529d06 id:long flags:# closed:flags.0?true question:string answers:Vector<PollAnswer> = Poll;
poll#d5529d06 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector<PollAnswer> = Poll;
pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true option:bytes voters:int = PollAnswerVoters;
pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true correct:flags.1?true option:bytes voters:int = PollAnswerVoters;
pollResults#5755785a flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int = PollResults;
pollResults#c87024a2 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<int> = PollResults;
chatOnlines#f041e250 onlines:int = ChatOnlines;
@@ -1077,16 +1079,11 @@ restrictionReason#d072acb4 platform:string reason:string text:string = Restricti
inputTheme#3c5693e9 id:long access_hash:long = InputTheme;
inputThemeSlug#f5890df1 slug:string = InputTheme;
themeDocumentNotModified#483d270c = Theme;
theme#28f1114 flags:# creator:flags.0?true default:flags.1?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?ThemeSettings installs_count:int = Theme;
account.themesNotModified#f41eb622 = account.Themes;
account.themes#7f676421 hash:int themes:Vector<Theme> = account.Themes;
wallet.liteResponse#764386d7 response:bytes = wallet.LiteResponse;
wallet.secretSalt#dd484d64 salt:bytes = wallet.KeySecretSalt;
auth.loginToken#629f1980 expires:int token:bytes = auth.LoginToken;
auth.loginTokenMigrateTo#68e9916 dc_id:int token:bytes = auth.LoginToken;
auth.loginTokenSuccess#390d5c5e authorization:auth.Authorization = auth.LoginToken;
@@ -1107,6 +1104,12 @@ themeSettings#9c14984a flags:# base_theme:BaseTheme accent_color:int message_top
webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector<Document> settings:flags.1?ThemeSettings = WebPageAttribute;
messageUserVote#a28e5559 user_id:int option:bytes date:int = MessageUserVote;
messageUserVoteInputOption#36377430 user_id:int date:int = MessageUserVote;
messageUserVoteMultiple#e8fe0de user_id:int options:Vector<bytes> date:int = MessageUserVote;
messages.votesList#823f649 flags:# count:int votes:Vector<MessageUserVote> users:Vector<User> next_offset:flags.0?string = messages.VotesList;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -1343,6 +1346,7 @@ messages.getScheduledHistory#e2c2685b peer:InputPeer hash:int = messages.Message
messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector<int> = messages.Messages;
messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector<int> = Updates;
messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector<int> = Updates;
messages.getPollVotes#b86e380e flags:# peer:InputPeer id:int option:flags.0?bytes offset:flags.1?string limit:int = messages.VotesList;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@@ -1354,7 +1358,7 @@ photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
upload.getFile#b15a9afc flags:# precise:flags.0?true location:InputFileLocation offset:int limit:int = upload.File;
upload.getFile#b15a9afc flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:int limit:int = upload.File;
upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile;
@@ -1451,7 +1455,4 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua
folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
folders.deleteFolder#1c295881 folder_id:int = Updates;
wallet.sendLiteRequest#e2c9d33e body:bytes = wallet.LiteResponse;
wallet.getKeySecretSalt#b57f346 revoke:Bool = wallet.KeySecretSalt;
// LAYER 108
// LAYER 109

View File

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

View File

@@ -33,8 +33,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,9,5,0
PRODUCTVERSION 1,9,5,0
FILEVERSION 1,9,17,0
PRODUCTVERSION 1,9,17,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -51,10 +51,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "1.9.5.0"
VALUE "FileVersion", "1.9.17.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.9.5.0"
VALUE "ProductVersion", "1.9.17.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -24,8 +24,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,9,5,0
PRODUCTVERSION 1,9,5,0
FILEVERSION 1,9,17,0
PRODUCTVERSION 1,9,17,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -42,10 +42,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "1.9.5.0"
VALUE "FileVersion", "1.9.17.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.9.5.0"
VALUE "ProductVersion", "1.9.17.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -21,6 +21,7 @@ struct SendOptions {
enum class SendType {
Normal,
Scheduled,
ScheduledToUser, // For "Send when online".
};
struct SendAction {

View File

@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h" // UserData::name
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "data/data_histories.h"
#include "history/history.h"
#include "history/history_message.h" // NewMessageFlags.
#include "chat_helpers/message_field.h" // ConvertTextTagsToEntities.
@@ -110,23 +111,30 @@ void SendExistingMedia(
auto failHandler = std::make_shared<Fn<void(const RPCError&, QByteArray)>>();
auto performRequest = [=] {
const auto usedFileReference = media->fileReference();
history->sendRequestId = api->request(MTPmessages_SendMedia(
MTP_flags(sendFlags),
peer->input,
MTP_int(replyTo),
inputMedia(),
MTP_string(captionText),
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities,
MTP_int(message.action.options.scheduled)
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result, randomId);
}).fail([=](const RPCError &error) {
(*failHandler)(error, usedFileReference);
}).afterRequest(history->sendRequestId
).send();
auto &histories = history->owner().histories();
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
const auto usedFileReference = media->fileReference();
history->sendRequestId = api->request(MTPmessages_SendMedia(
MTP_flags(sendFlags),
peer->input,
MTP_int(replyTo),
inputMedia(),
MTP_string(captionText),
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities,
MTP_int(message.action.options.scheduled)
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result, randomId);
finish();
}).fail([=](const RPCError &error) {
(*failHandler)(error, usedFileReference);
finish();
}).afterRequest(history->sendRequestId
).send();
return history->sendRequestId;
});
};
*failHandler = [=](const RPCError &error, QByteArray usedFileReference) {
if (error.code() == 400
@@ -169,7 +177,7 @@ void SendExistingDocument(
if (document->sticker()) {
if (const auto main = App::main()) {
main->incrementSticker(document);
document->session().data().notifyRecentStickersUpdated();
document->owner().notifyRecentStickersUpdated();
}
}
}

View File

@@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_cloud_themes.h"
#include "data/data_histories.h"
#include "dialogs/dialogs_key.h"
#include "core/core_cloud_password.h"
#include "core/application.h"
@@ -343,7 +344,7 @@ void ApiWrap::proxyPromotionDone(const MTPhelp_ProxyData &proxy) {
const auto peer = _session->data().peer(peerId);
_session->data().setProxyPromoted(peer);
if (const auto history = _session->data().historyLoaded(peer)) {
requestDialogEntry(history);
history->owner().histories().requestDialogEntry(history);
}
});
}
@@ -579,6 +580,13 @@ void ApiWrap::sendMessageFail(
requestFullPeer(peer);
}
}
} else if (error.type() == qstr("SCHEDULE_STATUS_PRIVATE")) {
auto &scheduled = _session->data().scheduledMessages();
Assert(peer->isUser());
if (const auto item = scheduled.lookupItem(peer->id, itemId.msg)) {
scheduled.removeSending(item);
Ui::show(Box<InformBox>(tr::lng_cant_do_this(tr::now)));
}
}
if (const auto item = _session->data().message(itemId)) {
Assert(randomId != 0);
@@ -1004,162 +1012,6 @@ rpl::producer<bool> ApiWrap::dialogsLoadBlockedByDate() const {
return _dialogsLoadBlockedByDate.value();
}
void ApiWrap::requestDialogEntry(not_null<Data::Folder*> folder) {
if (_dialogFolderRequests.contains(folder)) {
return;
}
_dialogFolderRequests.emplace(folder);
auto peers = QVector<MTPInputDialogPeer>(
1,
MTP_inputDialogPeerFolder(MTP_int(folder->id())));
request(MTPmessages_GetPeerDialogs(
MTP_vector(std::move(peers))
)).done([=](const MTPmessages_PeerDialogs &result) {
applyPeerDialogs(result);
_dialogFolderRequests.remove(folder);
}).fail([=](const RPCError &error) {
_dialogFolderRequests.remove(folder);
}).send();
}
void ApiWrap::requestDialogEntry(
not_null<History*> history,
Fn<void()> callback) {
const auto i = _dialogRequests.find(history);
if (i != end(_dialogRequests)) {
if (callback) {
i->second.push_back(std::move(callback));
}
return;
}
const auto [j, ok] = _dialogRequestsPending.try_emplace(history);
if (callback) {
j->second.push_back(std::move(callback));
}
if (!ok) {
return;
}
if (_dialogRequestsPending.size() > 1) {
return;
}
Core::App().postponeCall(crl::guard(_session, [=] {
sendDialogRequests();
}));
}
void ApiWrap::sendDialogRequests() {
if (_dialogRequestsPending.empty()) {
return;
}
auto histories = std::vector<not_null<History*>>();
ranges::transform(
_dialogRequestsPending,
ranges::back_inserter(histories),
[](const auto &pair) { return pair.first; });
auto peers = QVector<MTPInputDialogPeer>();
const auto dialogPeer = [](not_null<History*> history) {
return MTP_inputDialogPeer(history->peer->input);
};
ranges::transform(
histories,
ranges::back_inserter(peers),
dialogPeer);
for (auto &[history, callbacks] : base::take(_dialogRequestsPending)) {
_dialogRequests.emplace(history, std::move(callbacks));
}
const auto finalize = [=] {
for (const auto history : histories) {
dialogEntryApplied(history);
history->updateChatListExistence();
}
};
request(MTPmessages_GetPeerDialogs(
MTP_vector(std::move(peers))
)).done([=](const MTPmessages_PeerDialogs &result) {
applyPeerDialogs(result);
finalize();
}).fail([=](const RPCError &error) {
finalize();
}).send();
}
void ApiWrap::dialogEntryApplied(not_null<History*> history) {
history->dialogEntryApplied();
if (const auto callbacks = _dialogRequestsPending.take(history)) {
for (const auto &callback : *callbacks) {
callback();
}
}
if (const auto callbacks = _dialogRequests.take(history)) {
for (const auto &callback : *callbacks) {
callback();
}
}
}
void ApiWrap::applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs) {
Expects(dialogs.type() == mtpc_messages_peerDialogs);
const auto &data = dialogs.c_messages_peerDialogs();
_session->data().processUsers(data.vusers());
_session->data().processChats(data.vchats());
_session->data().processMessages(data.vmessages(), NewMessageType::Last);
for (const auto &dialog : data.vdialogs().v) {
dialog.match([&](const MTPDdialog &data) {
if (const auto peerId = peerFromMTP(data.vpeer())) {
_session->data().history(peerId)->applyDialog(nullptr, data);
}
}, [&](const MTPDdialogFolder &data) {
const auto folder = _session->data().processFolder(data.vfolder());
folder->applyDialog(data);
});
}
_session->data().sendHistoryChangeNotifications();
}
void ApiWrap::changeDialogUnreadMark(
not_null<History*> history,
bool unread) {
history->setUnreadMark(unread);
using Flag = MTPmessages_MarkDialogUnread::Flag;
request(MTPmessages_MarkDialogUnread(
MTP_flags(unread ? Flag::f_unread : Flag(0)),
MTP_inputDialogPeer(history->peer->input)
)).send();
}
void ApiWrap::requestFakeChatListMessage(
not_null<History*> history) {
if (_fakeChatListRequests.contains(history)) {
return;
}
_fakeChatListRequests.emplace(history);
request(MTPmessages_GetHistory(
history->peer->input,
MTP_int(0), // offset_id
MTP_int(0), // offset_date
MTP_int(0), // add_offset
MTP_int(2), // limit
MTP_int(0), // max_id
MTP_int(0), // min_id
MTP_int(0)
)).done([=](const MTPmessages_Messages &result) {
_fakeChatListRequests.erase(history);
history->setFakeChatListMessageFrom(result);
}).fail([=](const RPCError &error) {
_fakeChatListRequests.erase(history);
history->setFakeChatListMessageFrom(MTP_messages_messages(
MTP_vector<MTPMessage>(0),
MTP_vector<MTPChat>(0),
MTP_vector<MTPUser>(0)));
}).send();
}
void ApiWrap::requestWallPaper(
const QString &slug,
Fn<void(const Data::WallPaper &)> done,
@@ -1779,7 +1631,7 @@ void ApiWrap::requestSelfParticipant(not_null<ChannelData*> channel) {
history->checkLocalMessages();
history->owner().sendHistoryChangeNotifications();
} else {
requestDialogEntry(history);
history->owner().histories().requestDialogEntry(history);
}
}
};
@@ -2415,44 +2267,7 @@ int ApiWrap::OnlineTillFromStatus(
}
void ApiWrap::clearHistory(not_null<PeerData*> peer, bool revoke) {
auto deleteTillId = MsgId(0);
if (const auto history = _session->data().historyLoaded(peer)) {
while (history->lastMessageKnown()) {
const auto last = history->lastMessage();
if (!last) {
// History is empty.
return;
} else if (!IsServerMsgId(last->id)) {
// Destroy client-side message locally.
last->destroy();
} else {
break;
}
}
if (!history->lastMessageKnown()) {
requestDialogEntry(history, [=] {
Expects(history->lastMessageKnown());
clearHistory(peer, revoke);
});
return;
}
deleteTillId = history->lastMessage()->id;
history->clear(History::ClearType::ClearHistory);
}
if (const auto channel = peer->asChannel()) {
if (const auto migrated = peer->migrateFrom()) {
clearHistory(migrated, revoke);
}
if (IsServerMsgId(deleteTillId)) {
request(MTPchannels_DeleteHistory(
channel->inputChannel,
MTP_int(deleteTillId)
)).send();
}
} else {
deleteHistory(peer, true, revoke);
}
deleteHistory(peer, true, revoke);
}
void ApiWrap::deleteConversation(not_null<PeerData*> peer, bool revoke) {
@@ -2466,30 +2281,69 @@ void ApiWrap::deleteConversation(not_null<PeerData*> peer, bool revoke) {
}).fail([=](const RPCError &error) {
deleteHistory(peer, false, revoke);
}).send();
} else if (const auto channel = peer->asChannel()) {
channel->ptsWaitingForShortPoll(-1);
leaveChannel(channel);
} else {
deleteHistory(peer, false, revoke);
}
_session->data().deleteConversationLocally(peer);
}
void ApiWrap::deleteHistory(not_null<PeerData*> peer, bool justClear, bool revoke) {
using Flag = MTPmessages_DeleteHistory::Flag;
const auto flags = Flag(0)
| (justClear ? Flag::f_just_clear : Flag(0))
| ((peer->isUser() && revoke) ? Flag::f_revoke : Flag(0));
request(MTPmessages_DeleteHistory(
MTP_flags(flags),
peer->input,
MTP_int(0)
)).done([=](const MTPmessages_AffectedHistory &result) {
const auto offset = applyAffectedHistory(peer, result);
if (offset > 0) {
deleteHistory(peer, justClear, revoke);
void ApiWrap::deleteHistory(
not_null<PeerData*> peer,
bool justClear,
bool revoke) {
auto deleteTillId = MsgId(0);
const auto history = _session->data().history(peer);
if (justClear) {
// In case of clear history we need to know the last server message.
while (history->lastMessageKnown()) {
const auto last = history->lastMessage();
if (!last) {
// History is empty.
return;
} else if (!IsServerMsgId(last->id)) {
// Destroy client-side message locally.
last->destroy();
} else {
break;
}
}
}).send();
if (!history->lastMessageKnown()) {
history->owner().histories().requestDialogEntry(history, [=] {
Expects(history->lastMessageKnown());
deleteHistory(peer, justClear, revoke);
});
return;
}
deleteTillId = history->lastMessage()->id;
}
if (const auto channel = peer->asChannel()) {
if (!justClear) {
channel->ptsWaitingForShortPoll(-1);
leaveChannel(channel);
} else {
if (const auto migrated = peer->migrateFrom()) {
deleteHistory(migrated, justClear, revoke);
}
if (IsServerMsgId(deleteTillId)) {
history->owner().histories().deleteAllMessages(
history,
deleteTillId,
justClear,
revoke);
}
}
} else {
history->owner().histories().deleteAllMessages(
history,
deleteTillId,
justClear,
revoke);
}
if (!justClear) {
_session->data().deleteConversationLocally(peer);
} else if (history) {
history->clear(History::ClearType::ClearHistory);
}
}
int ApiWrap::applyAffectedHistory(
@@ -2521,30 +2375,6 @@ void ApiWrap::applyAffectedMessages(
App::main()->ptsUpdateAndApply(data.vpts().v, data.vpts_count().v);
}
void ApiWrap::deleteMessages(
not_null<PeerData*> peer,
const QVector<MTPint> &ids,
bool revoke) {
const auto done = [=](const MTPmessages_AffectedMessages & result) {
applyAffectedMessages(peer, result);
if (const auto history = peer->owner().historyLoaded(peer)) {
history->requestChatListMessage();
}
};
if (const auto channel = peer->asChannel()) {
request(MTPchannels_DeleteMessages(
channel->inputChannel,
MTP_vector<MTPint>(ids)
)).done(done).send();
} else {
using Flag = MTPmessages_DeleteMessages::Flag;
request(MTPmessages_DeleteMessages(
MTP_flags(revoke ? Flag::f_revoke : Flag(0)),
MTP_vector<MTPint>(ids)
)).done(done).send();
}
}
void ApiWrap::saveDraftsToCloud() {
for (auto i = _draftsSaveRequestIds.begin(), e = _draftsSaveRequestIds.end(); i != e; ++i) {
if (i->second) continue; // sent already
@@ -3652,10 +3482,22 @@ void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) {
auto &d = update.c_updateNewMessage();
auto needToAdd = true;
if (d.vmessage().type() == mtpc_message) { // index forwarded messages to links _overview
if (_session->data().checkEntitiesAndViewsUpdate(d.vmessage().c_message())) { // already in blocks
const auto &data = d.vmessage().c_message();
if (_session->data().checkEntitiesAndViewsUpdate(data)) { // already in blocks
LOG(("Skipping message, because it is already in blocks!"));
needToAdd = false;
}
if (needToAdd && !data.is_from_scheduled()) {
// If we still need to add a new message,
// we should first check if this message is in
// the list of scheduled messages.
// This is necessary to correctly update the file reference.
// Note that when a message is scheduled until online
// while the recipient is already online, the server sends
// an ordinary new message with skipped "from_scheduled" flag.
_session->data().scheduledMessages().checkEntitiesAndUpdate(
data);
}
}
if (needToAdd) {
_session->data().addNewMessage(
@@ -4038,12 +3880,12 @@ void ApiWrap::requestSharedMedia(
SharedMediaType type,
MsgId messageId,
SliceType slice) {
auto key = std::make_tuple(peer, type, messageId, slice);
const auto key = std::make_tuple(peer, type, messageId, slice);
if (_sharedMediaRequests.contains(key)) {
return;
}
auto prepared = Api::PrepareSearchRequest(
const auto prepared = Api::PrepareSearchRequest(
peer,
type,
QString(),
@@ -4053,17 +3895,23 @@ void ApiWrap::requestSharedMedia(
return;
}
auto requestId = request(
std::move(*prepared)
).done([this, peer, type, messageId, slice](
const MTPmessages_Messages &result) {
auto key = std::make_tuple(peer, type, messageId, slice);
_sharedMediaRequests.remove(key);
sharedMediaDone(peer, type, messageId, slice, result);
}).fail([this, key](const RPCError &error) {
_sharedMediaRequests.remove(key);
}).send();
_sharedMediaRequests.emplace(key, requestId);
const auto history = session().data().history(peer);
auto &histories = history->owner().histories();
const auto requestType = Data::Histories::RequestType::History;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
return request(
std::move(*prepared)
).done([=](const MTPmessages_Messages &result) {
const auto key = std::make_tuple(peer, type, messageId, slice);
_sharedMediaRequests.remove(key);
sharedMediaDone(peer, type, messageId, slice, result);
finish();
}).fail([=](const RPCError &error) {
_sharedMediaRequests.remove(key);
finish();
}).send();
});
_sharedMediaRequests.emplace(key);
}
void ApiWrap::sharedMediaDone(
@@ -4413,7 +4261,7 @@ void ApiWrap::userPhotosDone(
//}
void ApiWrap::sendAction(const SendAction &action) {
readServerHistory(action.history);
session().data().histories().readInbox(action.history);
action.history->getReadyFor(ShowAtTheEndMsgId);
_sendActions.fire_copy(action);
}
@@ -4424,6 +4272,8 @@ void ApiWrap::forwardMessages(
FnMut<void()> &&successCallback) {
Expects(!items.empty());
auto &histories = session().data().histories();
struct SharedCallback {
int requestsLeft = 0;
FnMut<void()> callback;
@@ -4440,7 +4290,7 @@ void ApiWrap::forwardMessages(
const auto history = action.history;
const auto peer = history->peer;
readServerHistory(history);
histories.readInbox(history);
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto silentPost = action.options.silent
@@ -4472,7 +4322,7 @@ void ApiWrap::forwardMessages(
auto currentGroupId = items.front()->groupId();
auto ids = QVector<MTPint>();
auto randomIds = QVector<MTPlong>();
auto localIds = std::unique_ptr<base::flat_map<uint64, FullMsgId>>();
auto localIds = std::shared_ptr<base::flat_map<uint64, FullMsgId>>();
const auto sendAccumulated = [&] {
if (shared) {
@@ -4482,30 +4332,36 @@ void ApiWrap::forwardMessages(
| (currentGroupId == MessageGroupId()
? MTPmessages_ForwardMessages::Flag(0)
: MTPmessages_ForwardMessages::Flag::f_grouped);
history->sendRequestId = request(MTPmessages_ForwardMessages(
MTP_flags(finalFlags),
forwardFrom->input,
MTP_vector<MTPint>(ids),
MTP_vector<MTPlong>(randomIds),
peer->input,
MTP_int(action.options.scheduled)
)).done([=, callback = std::move(successCallback)](
const MTPUpdates &updates) {
applyUpdates(updates);
if (shared && !--shared->requestsLeft) {
shared->callback();
}
}).fail([=, ids = std::move(localIds)](const RPCError &error) {
if (ids) {
for (const auto &[randomId, itemId] : *ids) {
sendMessageFail(error, peer, randomId, itemId);
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_ForwardMessages(
MTP_flags(finalFlags),
forwardFrom->input,
MTP_vector<MTPint>(ids),
MTP_vector<MTPlong>(randomIds),
peer->input,
MTP_int(action.options.scheduled)
)).done([=](
const MTPUpdates &updates) {
applyUpdates(updates);
if (shared && !--shared->requestsLeft) {
shared->callback();
}
} else {
sendMessageFail(error, peer);
}
}).afterRequest(
history->sendRequestId
).send();
finish();
}).fail([=, ids = localIds](const RPCError &error) {
if (ids) {
for (const auto &[randomId, itemId] : *ids) {
sendMessageFail(error, peer, randomId, itemId);
}
} else {
sendMessageFail(error, peer);
}
finish();
}).afterRequest(
history->sendRequestId
).send();
return history->sendRequestId;
});
ids.resize(0);
randomIds.resize(0);
@@ -4538,7 +4394,7 @@ void ApiWrap::forwardMessages(
message);
_session->data().registerMessageRandomId(randomId, newId);
if (!localIds) {
localIds = std::make_unique<base::flat_map<uint64, FullMsgId>>();
localIds = std::make_shared<base::flat_map<uint64, FullMsgId>>();
}
localIds->emplace(randomId, newId);
}
@@ -4953,6 +4809,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
HistoryItem *lastMessage = nullptr;
auto &histories = history->owner().histories();
const auto requestType = Data::Histories::RequestType::Send;
while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
auto newId = FullMsgId(
peerToChannel(peer->id),
@@ -5043,27 +4902,32 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
MTPVector<MTPRestrictionReason>()),
clientFlags,
NewMessageType::Unread);
history->sendRequestId = request(MTPmessages_SendMessage(
MTP_flags(sendFlags),
peer->input,
MTP_int(action.replyTo),
msgText,
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities,
MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result, randomId);
history->clearSentDraftText(QString());
}).fail([=](const RPCError &error) {
if (error.type() == qstr("MESSAGE_EMPTY")) {
lastMessage->destroy();
} else {
sendMessageFail(error, peer, randomId, newId);
}
history->clearSentDraftText(QString());
}).afterRequest(history->sendRequestId
).send();
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_SendMessage(
MTP_flags(sendFlags),
peer->input,
MTP_int(action.replyTo),
msgText,
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities,
MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result, randomId);
history->clearSentDraftText(QString());
finish();
}).fail([=](const RPCError &error) {
if (error.type() == qstr("MESSAGE_EMPTY")) {
lastMessage->destroy();
} else {
sendMessageFail(error, peer, randomId, newId);
}
history->clearSentDraftText(QString());
finish();
}).afterRequest(history->sendRequestId
).send();
return history->sendRequestId;
});
}
if (const auto main = App::main()) {
@@ -5169,23 +5033,29 @@ void ApiWrap::sendInlineResult(
history->clearCloudDraft();
history->setSentDraftText(QString());
history->sendRequestId = request(MTPmessages_SendInlineBotResult(
MTP_flags(sendFlags),
peer->input,
MTP_int(action.replyTo),
MTP_long(randomId),
MTP_long(data->getQueryId()),
MTP_string(data->getId()),
MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result, randomId);
history->clearSentDraftText(QString());
}).fail([=](const RPCError &error) {
sendMessageFail(error, peer, randomId, newId);
history->clearSentDraftText(QString());
}).afterRequest(history->sendRequestId
).send();
auto &histories = history->owner().histories();
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_SendInlineBotResult(
MTP_flags(sendFlags),
peer->input,
MTP_int(action.replyTo),
MTP_long(randomId),
MTP_long(data->getQueryId()),
MTP_string(data->getId()),
MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result, randomId);
history->clearSentDraftText(QString());
finish();
}).fail([=](const RPCError &error) {
sendMessageFail(error, peer, randomId, newId);
history->clearSentDraftText(QString());
finish();
}).afterRequest(history->sendRequestId
).send();
return history->sendRequestId;
});
if (const auto main = App::main()) {
main->finishForwarding(action);
}
@@ -5304,25 +5174,32 @@ void ApiWrap::sendMediaWithRandomId(
? MTPmessages_SendMedia::Flag::f_schedule_date
: MTPmessages_SendMedia::Flag(0));
const auto peer = history->peer;
const auto itemId = item->fullId();
history->sendRequestId = request(MTPmessages_SendMedia(
MTP_flags(flags),
peer->input,
MTP_int(replyTo),
media,
MTP_string(caption.text),
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities,
MTP_int(options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
}).fail([=](const RPCError &error) {
sendMessageFail(error, peer, randomId, itemId);
}).afterRequest(
history->sendRequestId
).send();
auto &histories = history->owner().histories();
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
const auto peer = history->peer;
const auto itemId = item->fullId();
history->sendRequestId = request(MTPmessages_SendMedia(
MTP_flags(flags),
peer->input,
MTP_int(replyTo),
media,
MTP_string(caption.text),
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities,
MTP_int(options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
finish();
}).fail([=](const RPCError &error) {
sendMessageFail(error, peer, randomId, itemId);
finish();
}).afterRequest(
history->sendRequestId
).send();
return history->sendRequestId;
});
}
void ApiWrap::sendAlbumWithUploaded(
@@ -5400,27 +5277,34 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
| (album->options.scheduled
? MTPmessages_SendMultiMedia::Flag::f_schedule_date
: MTPmessages_SendMultiMedia::Flag(0));
const auto peer = history->peer;
history->sendRequestId = request(MTPmessages_SendMultiMedia(
MTP_flags(flags),
peer->input,
MTP_int(replyTo),
MTP_vector<MTPInputSingleMedia>(medias),
MTP_int(album->options.scheduled)
)).done([=](const MTPUpdates &result) {
_sendingAlbums.remove(groupId);
applyUpdates(result);
}).fail([=](const RPCError &error) {
if (const auto album = _sendingAlbums.take(groupId)) {
for (const auto &item : (*album)->items) {
sendMessageFail(error, peer, item.randomId, item.msgId);
auto &histories = history->owner().histories();
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
const auto peer = history->peer;
history->sendRequestId = request(MTPmessages_SendMultiMedia(
MTP_flags(flags),
peer->input,
MTP_int(replyTo),
MTP_vector<MTPInputSingleMedia>(medias),
MTP_int(album->options.scheduled)
)).done([=](const MTPUpdates &result) {
_sendingAlbums.remove(groupId);
applyUpdates(result);
finish();
}).fail([=](const RPCError &error) {
if (const auto album = _sendingAlbums.take(groupId)) {
for (const auto &item : (*album)->items) {
sendMessageFail(error, peer, item.randomId, item.msgId);
}
} else {
sendMessageFail(error, peer);
}
} else {
sendMessageFail(error, peer);
}
}).afterRequest(
history->sendRequestId
).send();
finish();
}).afterRequest(
history->sendRequestId
).send();
return history->sendRequestId;
});
}
FileLoadTo ApiWrap::fileLoadTaskOptions(const SendAction &action) const {
@@ -5817,8 +5701,8 @@ Api::SensitiveContent &ApiWrap::sensitiveContent() {
void ApiWrap::createPoll(
const PollData &data,
const SendAction &action,
FnMut<void()> done,
FnMut<void(const RPCError &error)> fail) {
Fn<void()> done,
Fn<void(const RPCError &error)> fail) {
sendAction(action);
const auto history = action.history;
@@ -5842,24 +5726,43 @@ void ApiWrap::createPoll(
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
}
const auto replyTo = action.replyTo;
history->sendRequestId = request(MTPmessages_SendMedia(
MTP_flags(sendFlags),
peer->input,
MTP_int(replyTo),
MTP_inputMediaPoll(PollDataToMTP(&data)),
MTP_string(),
MTP_long(rand_value<uint64>()),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(),
MTP_int(action.options.scheduled)
)).done([=, done = std::move(done)](const MTPUpdates &result) mutable {
applyUpdates(result);
done();
}).fail([=, fail = std::move(fail)](const RPCError &error) mutable {
fail(error);
}).afterRequest(history->sendRequestId
).send();
const auto inputFlags = data.quiz()
? MTPDinputMediaPoll::Flag::f_correct_answers
: MTPDinputMediaPoll::Flag(0);
auto correct = QVector<MTPbytes>();
for (const auto &answer : data.answers) {
if (answer.correct) {
correct.push_back(MTP_bytes(answer.option));
}
}
auto &histories = history->owner().histories();
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
const auto replyTo = action.replyTo;
history->sendRequestId = request(MTPmessages_SendMedia(
MTP_flags(sendFlags),
peer->input,
MTP_int(replyTo),
MTP_inputMediaPoll(
MTP_flags(inputFlags),
PollDataToMTP(&data),
MTP_vector<MTPbytes>(correct)),
MTP_string(),
MTP_long(rand_value<uint64>()),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(),
MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) mutable {
applyUpdates(result);
done();
finish();
}).fail([=](const RPCError &error) mutable {
fail(error);
finish();
}).afterRequest(history->sendRequestId
).send();
return history->sendRequestId;
});
}
void ApiWrap::sendPollVotes(
@@ -5879,13 +5782,13 @@ void ApiWrap::sendPollVotes(
const auto hideSending = [=] {
if (showSending) {
if (const auto item = _session->data().message(itemId)) {
poll->sendingVote = QByteArray();
poll->sendingVotes.clear();
_session->data().requestItemRepaint(item);
}
}
};
if (showSending) {
poll->sendingVote = options.front();
poll->sendingVotes = options;
_session->data().requestItemRepaint(item);
}
@@ -5921,12 +5824,24 @@ void ApiWrap::closePoll(not_null<HistoryItem*> item) {
return;
}
const auto inputFlags = poll->quiz()
? MTPDinputMediaPoll::Flag::f_correct_answers
: MTPDinputMediaPoll::Flag(0);
auto correct = QVector<MTPbytes>();
for (const auto &answer : poll->answers) {
if (answer.correct) {
correct.push_back(MTP_bytes(answer.option));
}
}
const auto requestId = request(MTPmessages_EditMessage(
MTP_flags(MTPmessages_EditMessage::Flag::f_media),
item->history()->peer->input,
MTP_int(item->id),
MTPstring(),
MTP_inputMediaPoll(PollDataToMTP(poll)),
MTP_inputMediaPoll(
MTP_flags(inputFlags),
PollDataToMTP(poll, true),
MTP_vector<MTPbytes>(correct)),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(),
MTP_int(0) // schedule_date
@@ -5957,43 +5872,6 @@ void ApiWrap::reloadPollResults(not_null<HistoryItem*> item) {
_pollReloadRequestIds.emplace(itemId, requestId);
}
void ApiWrap::readServerHistory(not_null<History*> history) {
if (history->unreadCount()) {
readServerHistoryForce(history);
}
if (history->unreadMark()) {
changeDialogUnreadMark(history, false);
}
}
void ApiWrap::readServerHistoryForce(not_null<History*> history) {
const auto peer = history->peer;
const auto upTo = history->readInbox();
if (!upTo) {
return;
}
if (const auto channel = peer->asChannel()) {
if (!channel->amIn()) {
return; // no read request for channels that I didn't join
} else if (const auto migrateFrom = channel->migrateFrom()) {
if (const auto migrated = _session->data().historyLoaded(migrateFrom)) {
readServerHistory(migrated);
}
}
}
if (_readRequests.contains(peer)) {
const auto i = _readRequestsPending.find(peer);
if (i == _readRequestsPending.cend()) {
_readRequestsPending.emplace(peer, upTo);
} else if (i->second < upTo) {
i->second = upTo;
}
} else {
sendReadRequest(peer, upTo);
}
}
// // #feed
//void ApiWrap::readFeed(
// not_null<Data::Feed*> feed,
@@ -6047,39 +5925,3 @@ void ApiWrap::readServerHistoryForce(not_null<History*> history) {
// _feedReadTimer.callOnce(delay);
// }
//}
void ApiWrap::sendReadRequest(not_null<PeerData*> peer, MsgId upTo) {
const auto requestId = [&] {
const auto finished = [=] {
_readRequests.remove(peer);
if (const auto next = _readRequestsPending.take(peer)) {
sendReadRequest(peer, *next);
} else if (const auto history
= _session->data().historyLoaded(peer)) {
if (!history->unreadCountKnown()) {
requestDialogEntry(history);
}
}
};
if (const auto channel = peer->asChannel()) {
return request(MTPchannels_ReadHistory(
channel->inputChannel,
MTP_int(upTo)
)).done([=](const MTPBool &result) {
finished();
}).fail([=](const RPCError &error) {
finished();
}).send();
}
return request(MTPmessages_ReadHistory(
peer->input,
MTP_int(upTo)
)).done([=](const MTPmessages_AffectedMessages &result) {
applyAffectedMessages(peer, result);
finished();
}).fail([=](const RPCError &error) {
finished();
}).send();
}();
_readRequests.emplace(peer, requestId, upTo);
}

View File

@@ -141,6 +141,9 @@ public:
void applyUpdates(
const MTPUpdates &updates,
uint64 sentMessageRandomId = 0);
int applyAffectedHistory(
not_null<PeerData*> peer,
const MTPmessages_AffectedHistory &result);
void registerModifyRequest(const QString &key, mtpRequestId requestId);
void clearModifyRequest(const QString &key);
@@ -170,18 +173,10 @@ public:
rpl::producer<bool> dialogsLoadMayBlockByDate() const;
rpl::producer<bool> dialogsLoadBlockedByDate() const;
void requestDialogEntry(not_null<Data::Folder*> folder);
void requestDialogEntry(
not_null<History*> history,
Fn<void()> callback = nullptr);
void dialogEntryApplied(not_null<History*> history);
//void applyFeedSources(const MTPDchannels_feedSources &data); // #feed
//void setFeedChannels(
// not_null<Data::Feed*> feed,
// const std::vector<not_null<ChannelData*>> &channels);
void changeDialogUnreadMark(not_null<History*> history, bool unread);
//void changeDialogUnreadMark(not_null<Data::Feed*> feed, bool unread); // #feed
void requestFakeChatListMessage(not_null<History*> history);
void requestWallPaper(
const QString &slug,
@@ -305,10 +300,6 @@ public:
void clearHistory(not_null<PeerData*> peer, bool revoke);
void deleteConversation(not_null<PeerData*> peer, bool revoke);
void deleteMessages(
not_null<PeerData*> peer,
const QVector<MTPint> &ids,
bool revoke);
base::Observable<PeerData*> &fullPeerUpdated() {
return _fullPeerUpdated;
@@ -389,11 +380,12 @@ public:
const QString &lastName,
const SendAction &action);
void shareContact(not_null<UserData*> user, const SendAction &action);
void readServerHistory(not_null<History*> history);
void readServerHistoryForce(not_null<History*> history);
//void readFeed( // #feed
// not_null<Data::Feed*> feed,
// Data::MessagePosition position);
void applyAffectedMessages(
not_null<PeerData*> peer,
const MTPmessages_AffectedMessages &result);
void sendVoiceMessage(
QByteArray result,
@@ -475,8 +467,8 @@ public:
void createPoll(
const PollData &data,
const SendAction &action,
FnMut<void()> done,
FnMut<void(const RPCError &error)> fail);
Fn<void()> done,
Fn<void(const RPCError &error)> fail);
void sendPollVotes(
FullMsgId itemId,
const std::vector<QByteArray> &options);
@@ -531,7 +523,6 @@ private:
QVector<MTPInputMessage> collectMessageIds(const MessageDataRequests &requests);
MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false);
void applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs);
void gotChatFull(
not_null<PeerData*> peer,
@@ -623,14 +614,7 @@ private:
not_null<PeerData*> peer,
bool justClear,
bool revoke);
void sendReadRequest(not_null<PeerData*> peer, MsgId upTo);
int applyAffectedHistory(
not_null<PeerData*> peer,
const MTPmessages_AffectedHistory &result);
void applyAffectedMessages(const MTPmessages_AffectedMessages &result);
void applyAffectedMessages(
not_null<PeerData*> peer,
const MTPmessages_AffectedMessages &result);
void deleteAllFromUserSend(
not_null<ChannelData*> channel,
@@ -685,8 +669,6 @@ private:
not_null<ChannelData*> channel);
void migrateFail(not_null<PeerData*> peer, const RPCError &error);
void sendDialogRequests();
not_null<Main::Session*> _session;
base::flat_map<QString, int> _modifyRequests;
@@ -756,22 +738,14 @@ private:
mtpRequestId _contactsRequestId = 0;
mtpRequestId _contactsStatusesRequestId = 0;
base::flat_set<not_null<Data::Folder*>> _dialogFolderRequests;
base::flat_map<
not_null<History*>,
std::vector<Fn<void()>>> _dialogRequests;
base::flat_map<
not_null<History*>,
std::vector<Fn<void()>>> _dialogRequestsPending;
base::flat_set<not_null<History*>> _fakeChatListRequests;
base::flat_map<not_null<History*>, mtpRequestId> _unreadMentionsRequests;
base::flat_map<std::tuple<
base::flat_set<std::tuple<
not_null<PeerData*>,
SharedMediaType,
MsgId,
SliceType>, mtpRequestId> _sharedMediaRequests;
SliceType>> _sharedMediaRequests;
base::flat_map<not_null<UserData*>, mtpRequestId> _userPhotosRequests;
@@ -800,18 +774,6 @@ private:
rpl::event_stream<SendAction> _sendActions;
struct ReadRequest {
ReadRequest(mtpRequestId requestId, MsgId upTo)
: requestId(requestId)
, upTo(upTo) {
}
mtpRequestId requestId = 0;
MsgId upTo = 0;
};
base::flat_map<not_null<PeerData*>, ReadRequest> _readRequests;
base::flat_map<not_null<PeerData*>, MsgId> _readRequestsPending;
std::unique_ptr<TaskQueue> _fileLoader;
base::flat_map<uint64, std::shared_ptr<SendingAlbum>> _sendingAlbums;

View File

@@ -41,8 +41,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "numbers.h"
#include "observer_peer.h"
#include "main/main_session.h"
#include "styles/style_boxes.h"
#include "styles/style_overview.h"
#include "styles/style_mediaview.h"
#include "styles/style_media_view.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_history.h"
#include "styles/style_layers.h"
@@ -180,6 +181,8 @@ namespace App {
prepareCorners(MessageInSelectedCorners, st::historyMessageRadius, st::msgInBgSelected, &st::msgInShadowSelected);
prepareCorners(MessageOutCorners, st::historyMessageRadius, st::msgOutBg, &st::msgOutShadow);
prepareCorners(MessageOutSelectedCorners, st::historyMessageRadius, st::msgOutBgSelected, &st::msgOutShadowSelected);
prepareCorners(SendFilesBoxAlbumGroupCorners, st::sendBoxAlbumGroupRadius, st::callFingerprintBg);
}
void createCorners() {

View File

@@ -63,6 +63,8 @@ enum RoundCorners : int {
MessageOutCorners,
MessageOutSelectedCorners,
SendFilesBoxAlbumGroupCorners,
RoundCornersCount
};

View File

@@ -494,6 +494,7 @@ void GroupInfoBox::prepare() {
_description->setInstantReplaces(Ui::InstantReplaces::Default());
_description->setInstantReplacesEnabled(
_navigation->session().settings().replaceEmojiValue());
_description->setSubmitSettings(_navigation->session().settings().sendSubmitWay());
connect(_description, &Ui::InputField::resized, [=] { descriptionResized(); });
connect(_description, &Ui::InputField::submitted, [=] { submit(); });

View File

@@ -293,8 +293,7 @@ AdminLog::OwnedItem GenerateTextItem(
const auto clientFlags = MTPDmessage_ClientFlag::f_fake_history_item;
const auto replyTo = 0;
const auto viaBotId = 0;
const auto item = history->owner().makeMessage(
history,
const auto item = history->makeMessage(
++id,
flags,
clientFlags,

View File

@@ -514,6 +514,29 @@ editMediaButton: IconButton {
ripple: defaultRippleAnimation;
}
// SendFilesBox
sendBoxAlbumGroupEditInternalSkip: 9px;
sendBoxAlbumGroupSkipRight: 6px;
sendBoxAlbumGroupSkipTop: 6px;
sendBoxAlbumGroupRadius: 12px;
sendBoxAlbumGroupHeight: 25px;
sendBoxAlbumGroupEditButtonIcon: editMediaButtonIconPhoto;
sendBoxAlbumGroupEditButtonIconPosition: point(4px, -1px);
sendBoxAlbumGroupButtonFile: IconButton(editMediaButton) {
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgRipple;
}
}
sendBoxAlbumGroupDeleteButtonIconPosition: point(-3px, 0px);
sendBoxAlbumGroupDeleteButtonIcon: icon {{ "history_file_cancel", msgServiceFg}};
sendBoxAlbumGroupDeleteButtonIconFile: icon {{ "history_file_cancel", menuIconFg, point(6px, 6px) }};
// End of SendFilesBox
calendarTitleHeight: boxTitleHeight;
calendarPrevious: IconButton {
width: calendarTitleHeight;
@@ -812,15 +835,15 @@ createPollField: InputField(defaultInputField) {
}
createPollFieldPadding: margins(22px, 5px, 22px, 5px);
createPollOptionField: InputField(createPollField) {
textMargins: margins(22px, 8px, 40px, 8px);
textMargins: margins(22px, 11px, 40px, 11px);
placeholderMargins: margins(2px, 0px, 2px, 0px);
heightMax: 64px;
heightMax: 68px;
}
createPollLimitLabel: FlatLabel(defaultFlatLabel) {
minWidth: 274px;
align: align(topleft);
}
createPollLimitPadding: margins(22px, 10px, 22px, 5px);
createPollLimitPadding: margins(22px, 10px, 22px, 16px);
createPollOptionRemove: CrossButton {
width: 22px;
height: 22px;
@@ -841,7 +864,7 @@ createPollOptionRemove: CrossButton {
color: windowBgOver;
}
}
createPollOptionRemovePosition: point(10px, 7px);
createPollOptionRemovePosition: point(11px, 9px);
createPollWarning: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg;
palette: TextPalette(defaultTextPalette) {
@@ -849,6 +872,8 @@ createPollWarning: FlatLabel(defaultFlatLabel) {
}
}
createPollWarningPosition: point(16px, 6px);
createPollCheckboxMargin: margins(23px, 10px, 23px, 10px);
createPollFieldTitlePadding: margins(22px, 7px, 10px, 6px);
callSettingsButton: IconButton {
width: 50px;
@@ -921,3 +946,30 @@ customBadgeField: InputField(defaultInputField) {
heightMin: 32px;
}
pollResultsQuestion: FlatLabel(defaultFlatLabel) {
minWidth: 320px;
textFg: windowBoldFg;
style: TextStyle(defaultTextStyle) {
font: font(16px semibold);
linkFont: font(16px semibold);
linkFontOver: font(16px semibold underline);
}
}
pollResultsVotesCount: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg;
}
pollResultsHeaderPadding: margins(22px, 22px, 22px, 8px);
pollResultsShowMore: SettingsButton {
textFg: lightButtonFg;
textFgOver: lightButtonFgOver;
textBg: windowBg;
textBgOver: windowBgOver;
font: semiboldFont;
height: 20px;
padding: margins(71px, 10px, 8px, 8px);
ripple: defaultRippleAnimation;
}

View File

@@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_file_origin.h"
#include "data/data_histories.h"
#include "base/unixtime.h"
#include "main/main_session.h"
#include "observer_peer.h"
@@ -785,7 +786,9 @@ void DeleteMessagesBox::deleteAndClear() {
_deleteConfirmedCallback();
}
base::flat_map<not_null<PeerData*>, QVector<MTPint>> idsByPeer;
auto remove = std::vector<not_null<HistoryItem*>>();
remove.reserve(_ids.size());
base::flat_map<not_null<History*>, QVector<MTPint>> idsByPeer;
base::flat_map<not_null<PeerData*>, QVector<MTPint>> scheduledIdsByPeer;
for (const auto itemId : _ids) {
if (const auto item = _session->data().message(itemId)) {
@@ -801,21 +804,15 @@ void DeleteMessagesBox::deleteAndClear() {
}
continue;
}
const auto wasOnServer = IsServerMsgId(item->id);
const auto wasLast = (history->lastMessage() == item);
const auto wasInChats = (history->chatListMessage() == item);
item->destroy();
if (wasOnServer) {
idsByPeer[history->peer].push_back(MTP_int(itemId.msg));
} else if (wasLast || wasInChats) {
history->requestChatListMessage();
remove.push_back(item);
if (IsServerMsgId(item->id)) {
idsByPeer[history].push_back(MTP_int(itemId.msg));
}
}
}
for (const auto &[peer, ids] : idsByPeer) {
peer->session().api().deleteMessages(peer, ids, revoke);
for (const auto &[history, ids] : idsByPeer) {
history->owner().histories().deleteMessages(history, ids, revoke);
}
for (const auto &[peer, ids] : scheduledIdsByPeer) {
peer->session().api().request(MTPmessages_DeleteScheduledMessages(
@@ -826,6 +823,17 @@ void DeleteMessagesBox::deleteAndClear() {
}).send();
}
for (const auto item : remove) {
const auto history = item->history();
const auto wasLast = (history->lastMessage() == item);
const auto wasInChats = (history->chatListMessage() == item);
item->destroy();
if (wasLast || wasInChats) {
history->requestChatListMessage();
}
}
const auto session = _session;
Ui::hideLayer();
session->data().sendHistoryChangeNotifications();

View File

@@ -12,10 +12,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/toast/toast.h"
#include "main/main_session.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h"
@@ -42,13 +45,17 @@ public:
Options(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session);
not_null<Main::Session*> session,
bool chooseCorrectEnabled);
[[nodiscard]] bool hasOptions() const;
[[nodiscard]] bool isValid() const;
[[nodiscard]] rpl::producer<bool> isValidChanged() const;
[[nodiscard]] bool hasCorrect() const;
[[nodiscard]] std::vector<PollAnswer> toPollAnswers() const;
void focusFirst();
void enableChooseCorrect(bool enabled);
[[nodiscard]] rpl::producer<int> usedCount() const;
[[nodiscard]] rpl::producer<not_null<QWidget*>> scrollToWidget() const;
[[nodiscard]] rpl::producer<> backspaceInFront() const;
@@ -56,23 +63,31 @@ public:
private:
class Option {
public:
static Option Create(
Option(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session,
int position);
int position,
std::shared_ptr<Ui::RadiobuttonGroup> group);
Option(const Option &other) = delete;
Option &operator=(const Option &other) = delete;
void toggleRemoveAlways(bool toggled);
void enableChooseCorrect(
std::shared_ptr<Ui::RadiobuttonGroup> group);
void show(anim::type animated);
void destroy(FnMut<void()> done);
//[[nodisacrd]] bool hasShadow() const;
//void destroyShadow();
[[nodisacrd]] bool hasShadow() const;
void createShadow();
void destroyShadow();
[[nodiscard]] bool isEmpty() const;
[[nodiscard]] bool isGood() const;
[[nodiscard]] bool isTooLong() const;
[[nodiscard]] bool isCorrect() const;
[[nodiscard]] bool hasFocus() const;
void setFocus() const;
void clearValue();
@@ -86,29 +101,18 @@ private:
[[nodiscard]] rpl::producer<Qt::MouseButton> removeClicks() const;
inline bool operator<(const Option &other) const {
return field() < other.field();
}
friend inline bool operator<(
const Option &option,
Ui::InputField *field) {
return option.field() < field;
}
friend inline bool operator<(
Ui::InputField *field,
const Option &option) {
return field < option.field();
}
private:
Option() = default;
void createShadow();
void createRemove();
void createWarning();
void toggleCorrectSpace(bool visible);
void updateFieldGeometry();
base::unique_qptr<Ui::SlideWrap<Ui::InputField>> _field;
base::unique_qptr<Ui::SlideWrap<Ui::RpWidget>> _wrap;
not_null<Ui::RpWidget*> _content;
base::unique_qptr<Ui::FadeWrapScaled<Ui::Radiobutton>> _correct;
Ui::Animations::Simple _correctShown;
bool _hasCorrect = false;
Ui::InputField *_field = nullptr;
base::unique_qptr<Ui::PlainShadow> _shadow;
base::unique_qptr<Ui::CrossButton> _remove;
rpl::variable<bool> *_removeAlways = nullptr;
@@ -116,25 +120,30 @@ private:
};
[[nodiscard]] bool full() const;
//[[nodiscard]] bool correctShadows() const;
//void fixShadows();
[[nodiscard]] bool correctShadows() const;
void fixShadows();
void removeEmptyTail();
void addEmptyOption();
void checkLastOption();
void validateState();
void fixAfterErase();
void destroy(Option &&option);
void removeDestroyed(not_null<Ui::InputField*> field);
void destroy(std::unique_ptr<Option> option);
void removeDestroyed(not_null<Option*> field);
int findField(not_null<Ui::InputField*> field) const;
[[nodiscard]] auto createChooseCorrectGroup()
-> std::shared_ptr<Ui::RadiobuttonGroup>;
not_null<QWidget*> _outer;
not_null<Ui::VerticalLayout*> _container;
const not_null<Main::Session*> _session;
std::shared_ptr<Ui::RadiobuttonGroup> _chooseCorrectGroup;
int _position = 0;
std::vector<Option> _list;
std::set<Option, std::less<>> _destroyed;
rpl::variable<bool> _valid = false;
std::vector<std::unique_ptr<Option>> _list;
std::vector<std::unique_ptr<Option>> _destroyed;
rpl::variable<int> _usedCount = 0;
bool _hasOptions = false;
bool _isValid = false;
bool _hasCorrect = false;
rpl::event_stream<not_null<QWidget*>> _scrollToWidget;
rpl::event_stream<> _backspaceInFront;
@@ -187,58 +196,85 @@ void FocusAtEnd(not_null<Ui::InputField*> field) {
field->ensureCursorVisible();
}
Options::Option Options::Option::Create(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session,
int position) {
auto result = Option();
const auto field = container->insert(
position,
object_ptr<Ui::SlideWrap<Ui::InputField>>(
container,
object_ptr<Ui::InputField>(
container,
st::createPollOptionField,
Ui::InputField::Mode::NoNewlines,
tr::lng_polls_create_option_add())));
InitField(outer, field->entity(), session);
field->entity()->setMaxLength(kOptionLimit + kErrorLimit);
result._field.reset(field);
Options::Option::Option(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session,
int position,
std::shared_ptr<Ui::RadiobuttonGroup> group)
: _wrap(container->insert(
position,
object_ptr<Ui::SlideWrap<Ui::RpWidget>>(
container,
object_ptr<Ui::RpWidget>(container))))
, _content(_wrap->entity())
, _field(
Ui::CreateChild<Ui::InputField>(
_content.get(),
st::createPollOptionField,
Ui::InputField::Mode::NoNewlines,
tr::lng_polls_create_option_add())) {
InitField(outer, _field, session);
_field->setMaxLength(kOptionLimit + kErrorLimit);
_field->show();
result.createShadow();
result.createRemove();
result.createWarning();
return result;
_wrap->hide(anim::type::instant);
_content->widthValue(
) | rpl::start_with_next([=] {
updateFieldGeometry();
}, _field->lifetime());
_field->heightValue(
) | rpl::start_with_next([=](int height) {
_content->resize(_content->width(), height);
}, _field->lifetime());
QObject::connect(_field, &Ui::InputField::changed, [=] {
Ui::PostponeCall(crl::guard(_field, [=] {
if (_hasCorrect) {
_correct->toggle(isGood(), anim::type::normal);
}
}));
});
createShadow();
createRemove();
createWarning();
enableChooseCorrect(group);
_correctShown.stop();
if (_correct) {
_correct->finishAnimating();
}
updateFieldGeometry();
}
//bool Options::Option::hasShadow() const {
// return (_shadow != nullptr);
//}
bool Options::Option::hasShadow() const {
return (_shadow != nullptr);
}
void Options::Option::createShadow() {
Expects(_field != nullptr);
Expects(_content != nullptr);
if (_shadow) {
return;
}
const auto value = Ui::CreateChild<Ui::PlainShadow>(field().get());
value->show();
_shadow.reset(Ui::CreateChild<Ui::PlainShadow>(field().get()));
_shadow->show();
field()->sizeValue(
) | rpl::start_with_next([=](QSize size) {
const auto left = st::createPollFieldPadding.left();
value->setGeometry(
_shadow->setGeometry(
left,
size.height() - st::lineWidth,
size.width() - left,
st::lineWidth);
}, value->lifetime());
_shadow.reset(value);
}, _shadow->lifetime());
}
//void Options::Option::destroyShadow() {
// _shadow = nullptr;
//}
void Options::Option::destroyShadow() {
_shadow = nullptr;
}
void Options::Option::createRemove() {
using namespace rpl::mappers;
@@ -313,6 +349,10 @@ bool Options::Option::isTooLong() const {
return (field()->getLastText().size() > kOptionLimit);
}
bool Options::Option::isCorrect() const {
return isGood() && _correct && _correct->entity()->Checkbox::checked();
}
bool Options::Option::hasFocus() const {
return field()->hasFocus();
}
@@ -333,8 +373,66 @@ void Options::Option::toggleRemoveAlways(bool toggled) {
*_removeAlways = toggled;
}
void Options::Option::enableChooseCorrect(
std::shared_ptr<Ui::RadiobuttonGroup> group) {
if (!group) {
if (_correct) {
_hasCorrect = false;
_correct->hide(anim::type::normal);
toggleCorrectSpace(false);
}
return;
}
static auto Index = 0;
const auto button = Ui::CreateChild<Ui::FadeWrapScaled<Ui::Radiobutton>>(
_content.get(),
object_ptr<Ui::Radiobutton>(
_content.get(),
group,
++Index,
QString(),
st::defaultCheckbox));
button->entity()->resize(
button->entity()->height(),
button->entity()->height());
button->hide(anim::type::instant);
_content->sizeValue(
) | rpl::start_with_next([=](QSize size) {
const auto left = st::createPollFieldPadding.left();
button->moveToLeft(
left,
(size.height() - button->heightNoMargins()) / 2);
}, button->lifetime());
_correct.reset(button);
_hasCorrect = true;
if (isGood()) {
_correct->show(anim::type::normal);
} else {
_correct->hide(anim::type::instant);
}
toggleCorrectSpace(true);
}
void Options::Option::toggleCorrectSpace(bool visible) {
_correctShown.start(
[=] { updateFieldGeometry(); },
visible ? 0. : 1.,
visible ? 1. : 0.,
st::fadeWrapDuration);
}
void Options::Option::updateFieldGeometry() {
const auto shown = _correctShown.value(_hasCorrect ? 1. : 0.);
const auto skip = st::defaultRadio.diameter
+ st::defaultCheckbox.textPosition.x();
const auto left = anim::interpolate(0, skip, shown);
const auto width = _content->width() - left;
_field->resizeToWidth(_content->width() - left);
_field->moveToLeft(left, 0);
}
not_null<Ui::InputField*> Options::Option::field() const {
return _field->entity();
return _field;
}
void Options::Option::removePlaceholder() const {
@@ -344,10 +442,12 @@ void Options::Option::removePlaceholder() const {
PollAnswer Options::Option::toPollAnswer(int index) const {
Expects(index >= 0 && index < kMaxOptionsCount);
return PollAnswer{
auto result = PollAnswer{
field()->getLastText().trimmed(),
QByteArray(1, ('0' + index))
};
result.correct = _correct ? _correct->entity()->Checkbox::checked() : false;
return result;
}
rpl::producer<Qt::MouseButton> Options::Option::removeClicks() const {
@@ -357,10 +457,14 @@ rpl::producer<Qt::MouseButton> Options::Option::removeClicks() const {
Options::Options(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session)
not_null<Main::Session*> session,
bool chooseCorrectEnabled)
: _outer(outer)
, _container(container)
, _session(session)
, _chooseCorrectGroup(chooseCorrectEnabled
? createChooseCorrectGroup()
: nullptr)
, _position(_container->count()) {
checkLastOption();
}
@@ -369,12 +473,16 @@ bool Options::full() const {
return (_list.size() == kMaxOptionsCount);
}
bool Options::isValid() const {
return _valid.current();
bool Options::hasOptions() const {
return _hasOptions;
}
rpl::producer<bool> Options::isValidChanged() const {
return _valid.changes();
bool Options::isValid() const {
return _isValid;
}
bool Options::hasCorrect() const {
return _hasCorrect;
}
rpl::producer<int> Options::usedCount() const {
@@ -390,19 +498,18 @@ rpl::producer<> Options::backspaceInFront() const {
}
void Options::Option::show(anim::type animated) {
_field->hide(anim::type::instant);
_field->show(animated);
_wrap->show(animated);
}
void Options::Option::destroy(FnMut<void()> done) {
if (anim::Disabled() || _field->isHidden()) {
if (anim::Disabled() || _wrap->isHidden()) {
Ui::PostponeCall(std::move(done));
return;
}
_field->hide(anim::type::normal);
_wrap->hide(anim::type::normal);
base::call_delayed(
st::slideWrapDuration * 2,
_field.get(),
_content.get(),
std::move(done));
}
@@ -410,8 +517,8 @@ std::vector<PollAnswer> Options::toPollAnswers() const {
auto result = std::vector<PollAnswer>();
result.reserve(_list.size());
auto counter = int(0);
const auto makeAnswer = [&](const Option &option) {
return option.toPollAnswer(counter++);
const auto makeAnswer = [&](const std::unique_ptr<Option> &option) {
return option->toPollAnswer(counter++);
};
ranges::copy(
_list
@@ -424,29 +531,45 @@ std::vector<PollAnswer> Options::toPollAnswers() const {
void Options::focusFirst() {
Expects(!_list.empty());
_list.front().setFocus();
_list.front()->setFocus();
}
std::shared_ptr<Ui::RadiobuttonGroup> Options::createChooseCorrectGroup() {
auto result = std::make_shared<Ui::RadiobuttonGroup>(0);
result->setChangedCallback([=](int) {
validateState();
});
return result;
}
void Options::enableChooseCorrect(bool enabled) {
_chooseCorrectGroup = enabled
? createChooseCorrectGroup()
: nullptr;
for (auto &option : _list) {
option->enableChooseCorrect(_chooseCorrectGroup);
}
validateState();
}
bool Options::correctShadows() const {
// Last one should be without shadow.
const auto noShadow = ranges::find(
_list,
true,
ranges::not_fn(&Option::hasShadow));
return (noShadow == end(_list) - 1);
}
void Options::fixShadows() {
if (correctShadows()) {
return;
}
for (auto &option : _list) {
option->createShadow();
}
_list.back()->destroyShadow();
}
//
//bool Options::correctShadows() const {
// // Last one should be without shadow if all options were used.
// const auto noShadow = ranges::find(
// _list,
// true,
// ranges::not_fn(&Option::hasShadow));
// return (noShadow == end(_list) - (full() ? 1 : 0));
//}
//
//void Options::fixShadows() {
// if (correctShadows()) {
// return;
// }
// for (auto &option : _list) {
// option.createShadow();
// }
// if (full()) {
// _list.back().destroyShadow();
// }
//}
void Options::removeEmptyTail() {
// Only one option at the end of options list can be empty.
@@ -465,7 +588,7 @@ void Options::removeEmptyTail() {
return;
}
if (focusLast) {
emptyItem->setFocus();
(*emptyItem)->setFocus();
}
for (auto i = emptyItem + 1; i != end; ++i) {
destroy(std::move(*i));
@@ -474,44 +597,46 @@ void Options::removeEmptyTail() {
fixAfterErase();
}
void Options::destroy(Option &&option) {
const auto field = option.field();
option.destroy([=] { removeDestroyed(field); });
_destroyed.emplace(std::move(option));
void Options::destroy(std::unique_ptr<Option> option) {
const auto value = option.get();
option->destroy([=] { removeDestroyed(value); });
_destroyed.push_back(std::move(option));
}
void Options::fixAfterErase() {
Expects(!_list.empty());
const auto last = _list.end() - 1;
last->setPlaceholder();
last->toggleRemoveAlways(false);
(*last)->setPlaceholder();
(*last)->toggleRemoveAlways(false);
if (last != begin(_list)) {
(last - 1)->setPlaceholder();
(last - 1)->toggleRemoveAlways(false);
(*(last - 1))->setPlaceholder();
(*(last - 1))->toggleRemoveAlways(false);
}
fixShadows();
}
void Options::addEmptyOption() {
if (full()) {
return;
} else if (!_list.empty() && _list.back().isEmpty()) {
} else if (!_list.empty() && _list.back()->isEmpty()) {
return;
}
if (_list.size() > 1) {
(_list.end() - 2)->removePlaceholder();
(_list.end() - 2)->toggleRemoveAlways(true);
(*(_list.end() - 2))->removePlaceholder();
(*(_list.end() - 2))->toggleRemoveAlways(true);
}
_list.push_back(Option::Create(
_list.push_back(std::make_unique<Option>(
_outer,
_container,
_session,
_position + _list.size() + _destroyed.size()));
const auto field = _list.back().field();
_position + _list.size() + _destroyed.size(),
_chooseCorrectGroup));
const auto field = _list.back()->field();
QObject::connect(field, &Ui::InputField::submitted, [=] {
const auto index = findField(field);
if (_list[index].isGood() && index + 1 < _list.size()) {
_list[index + 1].setFocus();
if (_list[index]->isGood() && index + 1 < _list.size()) {
_list[index + 1]->setFocus();
}
});
QObject::connect(field, &Ui::InputField::changed, [=] {
@@ -534,25 +659,25 @@ void Options::addEmptyOption() {
const auto index = findField(field);
if (index > 0) {
_list[index - 1].setFocus();
_list[index - 1]->setFocus();
} else {
_backspaceInFront.fire({});
}
return base::EventFilterResult::Cancel;
});
_list.back().removeClicks(
) | rpl::start_with_next([=] {
_list.back()->removeClicks(
) | rpl::take(1) | rpl::start_with_next([=] {
Ui::PostponeCall(crl::guard(field, [=] {
Expects(!_list.empty());
const auto item = begin(_list) + findField(field);
if (item == _list.end() - 1) {
item->clearValue();
(*item)->clearValue();
return;
}
if (item->hasFocus()) {
(item + 1)->setFocus();
if ((*item)->hasFocus()) {
(*(item + 1))->setFocus();
}
destroy(std::move(*item));
_list.erase(item);
@@ -561,21 +686,29 @@ void Options::addEmptyOption() {
}));
}, field->lifetime());
_list.back().show((_list.size() == 1)
_list.back()->show((_list.size() == 1)
? anim::type::instant
: anim::type::normal);
//fixShadows();
fixShadows();
}
void Options::removeDestroyed(not_null<Ui::InputField*> field) {
_destroyed.erase(_destroyed.find(field));
void Options::removeDestroyed(not_null<Option*> option) {
const auto i = ranges::find(
_destroyed,
option.get(),
&std::unique_ptr<Option>::get);
Assert(i != end(_destroyed));
_destroyed.erase(i);
}
void Options::validateState() {
checkLastOption();
_valid = (ranges::count_if(_list, &Option::isGood) > 1)
_hasOptions = (ranges::count_if(_list, &Option::isGood) > 1);
_isValid = _hasOptions
&& (ranges::find_if(_list, &Option::isTooLong) == end(_list));
const auto lastEmpty = !_list.empty() && _list.back().isEmpty();
_hasCorrect = ranges::find_if(_list, &Option::isCorrect) != end(_list);
const auto lastEmpty = !_list.empty() && _list.back()->isEmpty();
_usedCount = _list.size() - (lastEmpty ? 1 : 0);
}
@@ -599,8 +732,12 @@ void Options::checkLastOption() {
CreatePollBox::CreatePollBox(
QWidget*,
not_null<Main::Session*> session,
PollData::Flags chosen,
PollData::Flags disabled,
Api::SendType sendType)
: _session(session)
, _chosen(chosen)
, _disabled(disabled)
, _sendType(sendType) {
}
@@ -630,6 +767,7 @@ not_null<Ui::InputField*> CreatePollBox::setupQuestion(
st::createPollFieldPadding);
InitField(getDelegate()->outerContainer(), question, _session);
question->setMaxLength(kQuestionLimit + kErrorLimit);
question->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
const auto warning = CreateWarningLabel(
container,
@@ -660,7 +798,7 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
using namespace Settings;
const auto id = rand_value<uint64>();
const auto valid = lifetime().make_state<rpl::event_stream<bool>>();
const auto error = lifetime().make_state<Errors>(Error::Question);
auto result = object_ptr<Ui::VerticalLayout>(this);
const auto container = result.data();
@@ -668,11 +806,17 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
const auto question = setupQuestion(container);
AddDivider(container);
AddSkip(container);
AddSubsectionTitle(container, tr::lng_polls_create_options());
container->add(
object_ptr<Ui::FlatLabel>(
container,
tr::lng_polls_create_options(),
st::settingsSubsectionTitle),
st::createPollFieldTitlePadding);
const auto options = lifetime().make_state<Options>(
getDelegate()->outerContainer(),
container,
_session);
_session,
(_chosen & PollData::Flag::Quiz));
auto limit = options->usedCount() | rpl::after_next([=](int count) {
setCloseByEscape(!count);
setCloseByOutsideClick(!count);
@@ -684,11 +828,68 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
container->resizeToWidth(container->widthNoMargins());
});
container->add(
object_ptr<Ui::FlatLabel>(
object_ptr<Ui::DividerLabel>(
container,
std::move(limit),
st::createPollLimitLabel),
st::createPollLimitPadding);
object_ptr<Ui::FlatLabel>(
container,
std::move(limit),
st::boxDividerLabel),
st::createPollLimitPadding));
AddSkip(container);
AddSubsectionTitle(container, tr::lng_polls_create_settings());
const auto anonymous = (!(_disabled & PollData::Flag::PublicVotes))
? container->add(
object_ptr<Ui::Checkbox>(
container,
tr::lng_polls_create_anonymous(tr::now),
!(_chosen & PollData::Flag::PublicVotes),
st::defaultCheckbox),
st::createPollCheckboxMargin)
: nullptr;
const auto hasMultiple = !(_chosen & PollData::Flag::Quiz)
|| !(_disabled & PollData::Flag::Quiz);
const auto multiple = hasMultiple
? container->add(
object_ptr<Ui::Checkbox>(
container,
tr::lng_polls_create_multiple_choice(tr::now),
(_chosen & PollData::Flag::MultiChoice),
st::defaultCheckbox),
st::createPollCheckboxMargin)
: nullptr;
const auto quiz = container->add(
object_ptr<Ui::Checkbox>(
container,
tr::lng_polls_create_quiz_mode(tr::now),
(_chosen & PollData::Flag::Quiz),
st::defaultCheckbox),
st::createPollCheckboxMargin);
quiz->setDisabled(_disabled & PollData::Flag::Quiz);
if (multiple) {
multiple->setDisabled((_disabled & PollData::Flag::MultiChoice)
|| (_chosen & PollData::Flag::Quiz));
multiple->events(
) | rpl::filter([=](not_null<QEvent*> e) {
return (e->type() == QEvent::MouseButtonPress) && quiz->checked();
}) | rpl::start_with_next([=] {
Ui::Toast::Show(tr::lng_polls_create_one_answer(tr::now));
}, multiple->lifetime());
}
using namespace rpl::mappers;
quiz->checkedChanges(
) | rpl::start_with_next([=](bool checked) {
if (multiple) {
if (checked && multiple->checked()) {
multiple->setChecked(false);
}
multiple->setDisabled(checked
|| (_disabled & PollData::Flag::MultiChoice));
}
options->enableChooseCorrect(checked);
}, quiz->lifetime());
const auto isValidQuestion = [=] {
const auto text = question->getLastText().trimmed();
@@ -706,13 +907,53 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
};
const auto collectResult = [=] {
auto result = PollData(id);
using Flag = PollData::Flag;
auto result = PollData(&_session->data(), id);
result.question = question->getLastText().trimmed();
result.answers = options->toPollAnswers();
const auto publicVotes = (anonymous && !anonymous->checked());
const auto multiChoice = (multiple && multiple->checked());
result.setFlags(Flag(0)
| (publicVotes ? Flag::PublicVotes : Flag(0))
| (multiChoice ? Flag::MultiChoice : Flag(0))
| (quiz->checked() ? Flag::Quiz : Flag(0)));
return result;
};
const auto send = [=](Api::SendOptions options) {
_submitRequests.fire({ collectResult(), options });
const auto collectError = [=] {
if (isValidQuestion()) {
*error &= ~Error::Question;
} else {
*error |= Error::Question;
}
if (!options->hasOptions()) {
*error |= Error::Options;
} else if (!options->isValid()) {
*error |= Error::Other;
} else {
*error &= ~(Error::Options | Error::Other);
}
if (quiz->checked() && !options->hasCorrect()) {
*error |= Error::Correct;
} else {
*error &= ~Error::Correct;
}
};
const auto showError = [=](const QString &text) {
Ui::Toast::Show(text);
};
const auto send = [=](Api::SendOptions sendOptions) {
collectError();
if (*error & Error::Question) {
showError(tr::lng_polls_choose_question(tr::now));
question->setFocus();
} else if (*error & Error::Options) {
showError(tr::lng_polls_choose_answers(tr::now));
options->focusFirst();
} else if (*error & Error::Correct) {
showError(tr::lng_polls_choose_correct(tr::now));
} else if (!*error) {
_submitRequests.fire({ collectResult(), sendOptions });
}
};
const auto sendSilent = [=] {
auto options = Api::SendOptions();
@@ -727,36 +968,6 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
send),
Ui::LayerOption::KeepOther);
};
const auto updateValid = [=] {
valid->fire(isValidQuestion() && options->isValid());
};
connect(question, &Ui::InputField::changed, [=] {
updateValid();
});
valid->events_starting_with(
false
) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](bool valid) {
clearButtons();
if (valid) {
const auto submit = addButton(
tr::lng_polls_create_button(),
[=] { send({}); });
if (_sendType == Api::SendType::Normal) {
SetupSendMenu(
submit.data(),
[=] { return SendMenuType::Scheduled; },
sendSilent,
sendScheduled);
}
}
addButton(tr::lng_cancel(), [=] { closeBox(); });
}, lifetime());
options->isValidChanged(
) | rpl::start_with_next([=] {
updateValid();
}, lifetime());
options->scrollToWidget(
) | rpl::start_with_next([=](not_null<QWidget*> widget) {
@@ -768,6 +979,22 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
FocusAtEnd(question);
}, lifetime());
const auto submit = addButton(
tr::lng_polls_create_button(),
[=] { send({}); });
if (_sendType == Api::SendType::Normal) {
const auto sendMenuType = [=] {
collectError();
return *error ? SendMenuType::Disabled : SendMenuType::Scheduled;
};
SetupSendMenuAndShortcuts(
submit.data(),
sendMenuType,
sendSilent,
sendScheduled);
}
addButton(tr::lng_cancel(), [=] { closeBox(); });
return result;
}

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
#include "api/api_common.h"
#include "data/data_poll.h"
#include "base/flags.h"
struct PollData;
@@ -31,6 +32,8 @@ public:
CreatePollBox(
QWidget*,
not_null<Main::Session*> session,
PollData::Flags chosen,
PollData::Flags disabled,
Api::SendType sendType);
rpl::producer<Result> submitRequests() const;
@@ -42,11 +45,22 @@ protected:
void prepare() override;
private:
enum class Error {
Question = 0x01,
Options = 0x02,
Correct = 0x04,
Other = 0x08,
};
friend constexpr inline bool is_flag_type(Error) { return true; }
using Errors = base::flags<Error>;
object_ptr<Ui::RpWidget> setupContent();
not_null<Ui::InputField*> setupQuestion(
not_null<Ui::VerticalLayout*> container);
const not_null<Main::Session*> _session;
const PollData::Flags _chosen = PollData::Flags();
const PollData::Flags _disabled = PollData::Flags();
const Api::SendType _sendType = Api::SendType();
Fn<void()> _setInnerFocus;
Fn<rpl::producer<bool>()> _dataIsValidValue;

View File

@@ -0,0 +1,442 @@
/*
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/dictionaries_manager.h"
#ifndef TDESKTOP_DISABLE_SPELLCHECK
#include "base/event_filter.h"
#include "chat_helpers/spellchecker_common.h"
#include "core/application.h"
#include "main/main_account.h"
#include "main/main_session.h"
#include "mainwidget.h"
#include "mtproto/dedicated_file_loader.h"
#include "spellcheck/spellcheck_utils.h"
#include "styles/style_layers.h"
#include "styles/style_settings.h"
#include "styles/style_boxes.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/multi_select.h"
#include "ui/widgets/popup_menu.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/effects/animations.h"
namespace Ui {
namespace {
using Dictionaries = std::vector<int>;
using namespace Storage::CloudBlob;
using Loading = MTP::DedicatedLoader::Progress;
using DictState = BlobState;
using QueryCallback = Fn<void(const QString &)>;
constexpr auto kMaxQueryLength = 15;
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
#define OLD_QT
using QStringView = QString;
#endif
class Inner : public Ui::RpWidget {
public:
Inner(QWidget *parent, Dictionaries enabledDictionaries);
Dictionaries enabledRows() const;
QueryCallback queryCallback() const;
private:
void setupContent(Dictionaries enabledDictionaries);
Dictionaries _enabledRows;
QueryCallback _queryCallback;
};
inline auto DictExists(int langId) {
return Spellchecker::DictionaryExists(langId);
}
inline auto FilterEnabledDict(Dictionaries dicts) {
return dicts | ranges::views::filter(
DictExists
) | ranges::to_vector;
}
DictState ComputeState(int id, bool enabled) {
const auto result = enabled ? DictState(Active()) : DictState(Ready());
if (DictExists(id)) {
return result;
}
return Available{ Spellchecker::GetDownloadSize(id) };
}
QString StateDescription(const DictState &state) {
return StateDescription(
state,
tr::lng_settings_manage_enabled_dictionary);
}
auto CreateMultiSelect(QWidget *parent) {
const auto result = Ui::CreateChild<Ui::MultiSelect>(
parent,
st::contactsMultiSelect,
tr::lng_participant_filter());
result->resizeToWidth(st::boxWidth);
result->moveToLeft(0, 0);
return result;
}
Inner::Inner(
QWidget *parent,
Dictionaries enabledDictionaries) : RpWidget(parent) {
setupContent(std::move(enabledDictionaries));
}
QueryCallback Inner::queryCallback() const {
return _queryCallback;
}
Dictionaries Inner::enabledRows() const {
return _enabledRows;
}
auto AddButtonWithLoader(
not_null<Ui::VerticalLayout*> content,
const Spellchecker::Dict &dict,
bool buttonEnabled,
rpl::producer<QStringView> query) {
const auto id = dict.id;
buttonEnabled &= DictExists(id);
const auto locale = Spellchecker::LocaleFromLangId(id);
const std::vector<QString> indexList = {
dict.name,
QLocale::languageToString(locale.language()),
QLocale::countryToString(locale.country())
};
const auto wrap = content->add(
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
content,
object_ptr<Ui::SettingsButton>(
content,
rpl::single(dict.name),
st::dictionariesSectionButton
)
)
);
const auto button = wrap->entity();
std::move(
query
) | rpl::start_with_next([=](auto string) {
wrap->toggle(
ranges::any_of(indexList, [&](const QString &s) {
return s.startsWith(string, Qt::CaseInsensitive);
}),
anim::type::instant);
}, button->lifetime());
using Loader = Spellchecker::DictLoader;
using GlobalLoaderPtr = std::shared_ptr<base::unique_qptr<Loader>>;
const auto localLoader = button->lifetime()
.make_state<base::unique_qptr<Loader>>();
const auto localLoaderValues = button->lifetime()
.make_state<rpl::event_stream<Loader*>>();
const auto setLocalLoader = [=](base::unique_qptr<Loader> loader) {
*localLoader = std::move(loader);
localLoaderValues->fire(localLoader->get());
};
const auto destroyLocalLoader = [=] {
setLocalLoader(nullptr);
};
const auto buttonState = button->lifetime()
.make_state<rpl::variable<DictState>>();
const auto dictionaryRemoved = button->lifetime()
.make_state<rpl::event_stream<>>();
const auto dictionaryFromGlobalLoader = button->lifetime()
.make_state<rpl::event_stream<>>();
const auto globalLoader = button->lifetime()
.make_state<GlobalLoaderPtr>();
const auto rawGlobalLoaderPtr = [=]() -> Loader* {
if (!globalLoader || !*globalLoader || !*globalLoader->get()) {
return nullptr;
}
return globalLoader->get()->get();
};
const auto setGlobalLoaderPtr = [=](GlobalLoaderPtr loader) {
if (localLoader->get()) {
if (loader && loader->get()) {
loader->get()->destroy();
}
return;
}
*globalLoader = std::move(loader);
localLoaderValues->fire(rawGlobalLoaderPtr());
if (rawGlobalLoaderPtr()) {
dictionaryFromGlobalLoader->fire({});
}
};
Spellchecker::GlobalLoaderChanged(
) | rpl::start_with_next([=](int langId) {
if (!langId && rawGlobalLoaderPtr()) {
setGlobalLoaderPtr(nullptr);
} else if (langId == id) {
setGlobalLoaderPtr(Spellchecker::GlobalLoader());
}
}, button->lifetime());
const auto label = Ui::CreateChild<Ui::FlatLabel>(
button,
buttonState->value() | rpl::map(StateDescription),
st::settingsUpdateState);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
rpl::combine(
button->widthValue(),
label->widthValue()
) | rpl::start_with_next([=] {
label->moveToLeft(
st::settingsUpdateStatePosition.x(),
st::settingsUpdateStatePosition.y());
}, label->lifetime());
buttonState->value(
) | rpl::start_with_next([=](const DictState &state) {
const auto isToggledSet = state.is<Active>();
const auto toggled = isToggledSet ? 1. : 0.;
const auto over = !button->isDisabled()
&& (button->isDown() || button->isOver());
if (toggled == 0. && !over) {
label->setTextColorOverride(std::nullopt);
} else {
label->setTextColorOverride(anim::color(
over ? st::contactsStatusFgOver : st::contactsStatusFg,
st::contactsStatusFgOnline,
toggled));
}
}, label->lifetime());
button->toggleOn(
rpl::single(
buttonEnabled
) | rpl::then(
rpl::merge(
// Events to toggle on.
dictionaryFromGlobalLoader->events(
) | rpl::map([] {
return true;
}),
// Events to toggle off.
rpl::merge(
dictionaryRemoved->events(),
buttonState->value(
) | rpl::filter([](const DictState &state) {
return state.is<Failed>();
}) | rpl::map([] {
return rpl::empty_value();
})
) | rpl::map([] {
return false;
})
)
)
);
*buttonState = localLoaderValues->events_starting_with(
rawGlobalLoaderPtr() ? rawGlobalLoaderPtr() : localLoader->get()
) | rpl::map([=](Loader *loader) {
return (loader && loader->id() == id)
? loader->state()
: rpl::single(
buttonEnabled
) | rpl::then(
rpl::merge(
dictionaryRemoved->events(
) | rpl::map([] {
return false;
}),
button->toggledValue()
)
) | rpl::map([=](auto enabled) {
return ComputeState(id, enabled);
});
}) | rpl::flatten_latest(
) | rpl::filter([=](const DictState &state) {
return !buttonState->current().is<Failed>() || !state.is<Available>();
});
button->toggledValue(
) | rpl::start_with_next([=](bool toggled) {
const auto &state = buttonState->current();
if (toggled && (state.is<Available>() || state.is<Failed>())) {
const auto weak = Ui::MakeWeak(button);
setLocalLoader(base::make_unique_q<Loader>(
App::main(),
id,
Spellchecker::GetDownloadLocation(id),
Spellchecker::DictPathByLangId(id),
Spellchecker::GetDownloadSize(id),
crl::guard(weak, destroyLocalLoader)));
} else if (!toggled && state.is<Loading>()) {
if (const auto g = rawGlobalLoaderPtr()) {
g->destroy();
return;
}
if (localLoader && localLoader->get()->id() == id) {
destroyLocalLoader();
}
}
}, button->lifetime());
const auto contextMenu = button->lifetime()
.make_state<base::unique_qptr<Ui::PopupMenu>>();
const auto showMenu = [=] {
if (!DictExists(id)) {
return false;
}
*contextMenu = base::make_unique_q<Ui::PopupMenu>(button);
contextMenu->get()->addAction(
tr::lng_settings_manage_remove_dictionary(tr::now), [=] {
Spellchecker::RemoveDictionary(id);
dictionaryRemoved->fire({});
});
contextMenu->get()->popup(QCursor::pos());
return true;
};
base::install_event_filter(button, [=](not_null<QEvent*> e) {
if (e->type() == QEvent::ContextMenu && showMenu()) {
return base::EventFilterResult::Cancel;
}
return base::EventFilterResult::Continue;
});
if (const auto g = Spellchecker::GlobalLoader()) {
if (g.get() && g->get()->id() == id) {
setGlobalLoaderPtr(g);
}
}
return button;
}
void Inner::setupContent(Dictionaries enabledDictionaries) {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
const auto queryStream = content->lifetime()
.make_state<rpl::event_stream<QStringView>>();
for (const auto &dict : Spellchecker::Dictionaries()) {
const auto id = dict.id;
const auto row = AddButtonWithLoader(
content,
dict,
ranges::contains(enabledDictionaries, id),
queryStream->events());
row->toggledValue(
) | rpl::start_with_next([=](auto enabled) {
if (enabled) {
_enabledRows.push_back(id);
} else {
auto &rows = _enabledRows;
rows.erase(ranges::remove(rows, id), end(rows));
}
}, row->lifetime());
}
_queryCallback = [=](const QString &query) {
if (query.size() >= kMaxQueryLength) {
return;
}
queryStream->fire_copy(query);
};
content->resizeToWidth(st::boxWidth);
Ui::ResizeFitChild(this, content);
}
} // namespace
ManageDictionariesBox::ManageDictionariesBox(
QWidget*,
not_null<Main::Session*> session)
: _session(session) {
}
void ManageDictionariesBox::setInnerFocus() {
_setInnerFocus();
}
void ManageDictionariesBox::prepare() {
const auto multiSelect = CreateMultiSelect(this);
const auto inner = setInnerWidget(
object_ptr<Inner>(
this,
_session->settings().dictionariesEnabled()),
st::boxScroll,
multiSelect->height()
);
multiSelect->setQueryChangedCallback(inner->queryCallback());
_setInnerFocus = [=] {
multiSelect->setInnerFocus();
};
// The initial list of enabled rows may differ from the list of languages
// in settings, so we should store it when box opens
// and save it when box closes (don't do it when "Save" was pressed).
const auto initialEnabledRows = inner->enabledRows();
setTitle(tr::lng_settings_manage_dictionaries());
addButton(tr::lng_settings_save(), [=] {
_session->settings().setDictionariesEnabled(
FilterEnabledDict(inner->enabledRows()));
_session->saveSettingsDelayed();
// Ignore boxClosing() when the Save button was pressed.
lifetime().destroy();
closeBox();
});
addButton(tr::lng_close(), [=] { closeBox(); });
boxClosing() | rpl::start_with_next([=] {
_session->settings().setDictionariesEnabled(
FilterEnabledDict(initialEnabledRows));
_session->saveSettingsDelayed();
}, lifetime());
setDimensionsToContent(st::boxWidth, inner);
using namespace rpl::mappers;
const auto max = lifetime().make_state<int>(0);
rpl::combine(
inner->heightValue(),
multiSelect->heightValue(),
_1 + _2
) | rpl::start_with_next([=](int height) {
using std::min;
accumulate_max(*max, height);
setDimensions(st::boxWidth, min(*max, st::boxMaxListHeight), true);
}, inner->lifetime());
}
} // namespace Ui
#endif // !TDESKTOP_DISABLE_SPELLCHECK

View File

@@ -0,0 +1,38 @@
/*
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
#ifndef TDESKTOP_DISABLE_SPELLCHECK
#include "boxes/abstract_box.h"
namespace Main {
class Session;
} // namespace Main
namespace Ui {
class ManageDictionariesBox : public Ui::BoxContent {
public:
ManageDictionariesBox(
QWidget*,
not_null<Main::Session*> session);
protected:
void prepare() override;
void setInnerFocus() override;
private:
const not_null<Main::Session*> _session;
Fn<void()> _setInnerFocus;
};
} // namespace Ui
#endif // !TDESKTOP_DISABLE_SPELLCHECK

View File

@@ -260,7 +260,7 @@ EditCaptionBox::EditCaptionBox(
tr::lng_photo_caption(),
editData);
_field->setMaxLength(Global::CaptionLengthMax());
_field->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
_field->setSubmitSettings(_controller->session().settings().sendSubmitWay());
_field->setInstantReplaces(Ui::InstantReplaces::Default());
_field->setInstantReplacesEnabled(
_controller->session().settings().replaceEmojiValue());
@@ -488,97 +488,30 @@ void EditCaptionBox::updateEditMediaButton() {
void EditCaptionBox::createEditMediaButton() {
const auto callback = [=](FileDialog::OpenResult &&result) {
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
return;
}
const auto isValidFile = [](QString mimeType) {
if (mimeType == qstr("image/webp")) {
Ui::show(
Box<InformBox>(tr::lng_edit_media_invalid_file(tr::now)),
Ui::LayerOption::KeepOther);
return false;
}
return true;
auto showBoxErrorCallback = [](tr::phrase<> t) {
Ui::show(Box<InformBox>(t(tr::now)), Ui::LayerOption::KeepOther);
};
if (!result.remoteContent.isEmpty()) {
auto list = Storage::PreparedList::PreparedFileFromFilesDialog(
std::move(result),
_isAlbum,
std::move(showBoxErrorCallback),
st::sendMediaPreviewSize);
auto list = Storage::PrepareMediaFromImage(
QImage(),
std::move(result.remoteContent),
st::sendMediaPreviewSize);
if (!isValidFile(list.files.front().mime)) {
return;
}
if (_isAlbum) {
const auto albumMimes = {
"image/jpeg",
"image/png",
"video/mp4",
};
const auto file = &list.files.front();
if (ranges::find(albumMimes, file->mime) == end(albumMimes)
|| file->type == Storage::PreparedFile::AlbumType::None) {
Ui::show(
Box<InformBox>(tr::lng_edit_media_album_error(tr::now)),
Ui::LayerOption::KeepOther);
return;
}
}
_preparedList = std::move(list);
} else if (!result.paths.isEmpty()) {
auto list = Storage::PrepareMediaList(
QStringList(result.paths.front()),
st::sendMediaPreviewSize);
// Don't rewrite _preparedList if new list is not valid for album.
if (_isAlbum) {
using Info = FileMediaInformation;
const auto media = &list.files.front().information->media;
const auto valid = media->match([&](const Info::Image &data) {
return Storage::ValidateThumbDimensions(
data.data.width(),
data.data.height())
&& !data.animated;
}, [&](Info::Video &data) {
data.isGifv = false;
return true;
}, [](auto &&other) {
return false;
});
if (!valid) {
Ui::show(
Box<InformBox>(tr::lng_edit_media_album_error(tr::now)),
Ui::LayerOption::KeepOther);
return;
}
}
const auto info = QFileInfo(result.paths.front());
if (!isValidFile(Core::MimeTypeForFile(info).name())) {
return;
}
_preparedList = std::move(list);
} else {
return;
if (list) {
_preparedList = std::move(*list);
updateEditPreview();
}
updateEditPreview();
};
const auto buttonCallback = [=] {
const auto filters = _isAlbum
? QStringList(qsl("Image and Video Files (*.png *.jpg *.mp4)"))
: QStringList(FileDialog::AllFilesFilter());
? FileDialog::AlbumFilesFilter()
: FileDialog::AllFilesFilter();
FileDialog::GetOpenPath(
this,
tr::lng_choose_file(tr::now),
filters.join(qsl(";;")),
filters,
crl::guard(this, callback));
};

View File

@@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_info.h"
#include "app.h"
#include "styles/style_boxes.h"
#include "styles/style_mediaview.h"
#include "styles/style_media_view.h"
class EditColorBox::Picker : public TWidget {
public:

View File

@@ -76,7 +76,7 @@ void MuteSettingsBox::prepare() {
_save = [=] {
const auto muteForSeconds = group->value() * 3600;
_peer->session().data().updateNotifySettings(
_peer->owner().updateNotifySettings(
_peer,
muteForSeconds);
closeBox();

View File

@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_folder.h"
#include "data/data_histories.h"
#include "apiwrap.h"
#include "mainwidget.h"
#include "lang/lang_keys.h"
@@ -30,33 +31,36 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) {
const auto history = chat->owner().historyLoaded(chat);
const auto randomId = rand_value<uint64>();
const auto api = &chat->session().api();
const auto requestId = api->request(MTPmessages_SendMedia(
MTP_flags(0),
chat->input,
MTP_int(0),
MTP_inputMediaGame(
MTP_inputGameShortName(
bot->inputUser,
MTP_string(bot->botInfo->shareGameShortName))),
MTP_string(),
MTP_long(randomId),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(),
MTP_int(0) // schedule_date
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result, randomId);
}).fail([=](const RPCError &error) {
api->sendMessageFail(error, chat);
}).afterRequest(
history ? history->sendRequestId : 0
).send();
if (history) {
history->sendRequestId = requestId;
}
const auto history = chat->owner().history(chat);
auto &histories = history->owner().histories();
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
const auto randomId = rand_value<uint64>();
const auto api = &chat->session().api();
history->sendRequestId = api->request(MTPmessages_SendMedia(
MTP_flags(0),
chat->input,
MTP_int(0),
MTP_inputMediaGame(
MTP_inputGameShortName(
bot->inputUser,
MTP_string(bot->botInfo->shareGameShortName))),
MTP_string(),
MTP_long(randomId),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(),
MTP_int(0) // schedule_date
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result, randomId);
finish();
}).fail([=](const RPCError &error) {
api->sendMessageFail(error, chat);
finish();
}).afterRequest(
history->sendRequestId
).send();
return history->sendRequestId;
});
Ui::hideLayer();
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
}

View File

@@ -500,6 +500,7 @@ object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
result->entity()->setInstantReplaces(Ui::InstantReplaces::Default());
result->entity()->setInstantReplacesEnabled(
_peer->session().settings().replaceEmojiValue());
result->entity()->setSubmitSettings(_peer->session().settings().sendSubmitWay());
Ui::Emoji::SuggestionsController::Init(
_wrap->window(),
result->entity(),

View File

@@ -85,7 +85,7 @@ void RateCallBox::ratingChanged(int value) {
Ui::InputField::Mode::MultiLine,
tr::lng_call_rate_comment());
_comment->show();
_comment->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
_comment->setSubmitSettings(_session->settings().sendSubmitWay());
_comment->setMaxLength(kRateCallCommentLengthMax);
_comment->resize(width() - (st::callRatingPadding.left() + st::callRatingPadding.right()), _comment->height());

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "data/data_peer.h"
#include "main/main_session.h"
#include "boxes/confirm_box.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
@@ -106,7 +107,7 @@ void ReportBox::reasonChanged(Reason reason) {
Ui::InputField::Mode::MultiLine,
tr::lng_report_reason_description());
_reasonOtherText->show();
_reasonOtherText->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
_reasonOtherText->setSubmitSettings(_peer->session().settings().sendSubmitWay());
_reasonOtherText->setMaxLength(kReportReasonLengthMax);
_reasonOtherText->resize(width() - (st::boxPadding.left() + st::boxOptionListPadding.left() + st::boxPadding.right()), _reasonOtherText->height());

View File

@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "confirm_box.h"
#include "history/view/history_view_schedule_box.h"
#include "core/file_utilities.h"
#include "core/mime_type.h"
@@ -48,8 +49,102 @@ namespace {
constexpr auto kMinPreviewWidth = 20;
constexpr auto kShrinkDuration = crl::time(150);
constexpr auto kDragDuration = crl::time(200);
const auto kStickerMimeString = qstr("image/webp");
const auto kAnimatedStickerMimeString = qstr("application/x-tgsticker");
enum class ButtonType {
Edit,
Delete,
None,
};
inline bool CanAddUrls(const QList<QUrl> &urls) {
return !urls.isEmpty() && ranges::find_if(
urls,
[](const QUrl &url) { return !url.isLocalFile(); }
) == urls.end();
}
inline bool IsFirstAlbumItem(const Storage::PreparedList &list) {
using AlbumType = Storage::PreparedFile::AlbumType;
return (list.files.size() > 0)
&& (list.files.front().type != AlbumType::None);
}
inline bool IsSingleItem(const Storage::PreparedList &list) {
return list.files.size() == 1;
}
QRect PaintAlbumThumbButtons(
Painter &p,
QPoint point,
int outerWidth,
float64 shrinkProgress) {
const auto skipInternal = st::sendBoxAlbumGroupEditInternalSkip;
const auto size = st::sendBoxAlbumGroupHeight;
const auto skipRight = st::sendBoxAlbumGroupSkipRight;
const auto skipTop = st::sendBoxAlbumGroupSkipTop;
const auto groupWidth = size * 2 + skipInternal;
// If the width is tiny, it would be better to not display the buttons.
if (groupWidth > outerWidth) {
return QRect();
}
// If the width is too small,
// it would be better to display the buttons in the center.
const auto groupX = point.x() + ((groupWidth + skipRight * 2 > outerWidth)
? (outerWidth - groupWidth) / 2
: outerWidth - skipRight - groupWidth);
const auto groupY = point.y() + skipTop;
const auto deleteLeft = skipInternal + size;
p.setOpacity(1.0 - shrinkProgress);
QRect groupRect(groupX, groupY, groupWidth, size);
App::roundRect(
p,
groupRect,
st::callFingerprintBg,
SendFilesBoxAlbumGroupCorners);
const auto editP = st::sendBoxAlbumGroupEditButtonIconPosition;
const auto deleteP = st::sendBoxAlbumGroupDeleteButtonIconPosition;
st::sendBoxAlbumGroupEditButtonIcon.paintInCenter(
p,
QRect(groupX + editP.x(), groupY + editP.y(), size, size));
st::sendBoxAlbumGroupDeleteButtonIcon.paintInCenter(
p,
QRect(
groupX + deleteLeft + deleteP.x(),
groupY + deleteP.y(),
size,
size));
p.setOpacity(1);
return groupRect;
}
void FileDialogCallback(
FileDialog::OpenResult &&result,
bool isAlbum,
Fn<void(Storage::PreparedList)> callback) {
auto showBoxErrorCallback = [](tr::phrase<> text) {
Ui::show(Box<InformBox>(text(tr::now)), Ui::LayerOption::KeepOther);
};
auto list = Storage::PreparedList::PreparedFileFromFilesDialog(
std::move(result),
isAlbum,
std::move(showBoxErrorCallback),
st::sendMediaPreviewSize);
if (!list) {
return;
}
callback(std::move(*list));
}
class SingleMediaPreview : public Ui::RpWidget {
public:
@@ -123,7 +218,10 @@ class AlbumThumb {
public:
AlbumThumb(
const Storage::PreparedFile &file,
const Ui::GroupMediaLayout &layout);
const Ui::GroupMediaLayout &layout,
QWidget *parent,
Fn<void()> editCallback,
Fn<void()> deleteCallback);
void moveToLayout(const Ui::GroupMediaLayout &layout);
void animateLayoutToInitial();
@@ -141,6 +239,8 @@ public:
void paintFile(Painter &p, int left, int top, int outerWidth);
bool containsPoint(QPoint position) const;
bool buttonsContainPoint(QPoint position) const;
ButtonType buttonTypeFromPoint(QPoint position) const;
int distanceTo(QPoint position) const;
bool isPointAfter(QPoint position) const;
void moveInAlbum(QPoint to);
@@ -148,6 +248,8 @@ public:
void suggestMove(float64 delta, Fn<void()> callback);
void finishAnimations();
void updateFileRow(int row);
private:
QRect countRealGeometry() const;
QRect countCurrentGeometry(float64 progress) const;
@@ -173,11 +275,19 @@ private:
Ui::Animations::Simple _suggestedMoveAnimation;
int _lastShrinkValue = 0;
QRect _lastRectOfButtons;
object_ptr<Ui::IconButton> _editMedia = nullptr;
object_ptr<Ui::IconButton> _deleteMedia = nullptr;
};
AlbumThumb::AlbumThumb(
const Storage::PreparedFile &file,
const Ui::GroupMediaLayout &layout)
const Ui::GroupMediaLayout &layout,
QWidget *parent,
Fn<void()> editCallback,
Fn<void()> deleteCallback)
: _layout(layout)
, _fullPreview(file.preview)
, _shrinkSize(int(std::ceil(st::historyMessageRadius / 1.4)))
@@ -218,7 +328,11 @@ AlbumThumb::AlbumThumb(
const auto availableFileWidth = st::sendMediaPreviewSize
- st::sendMediaFileThumbSkip
- st::sendMediaFileThumbSize;
- st::sendMediaFileThumbSize
// Right buttons.
- st::sendBoxAlbumGroupButtonFile.width * 2
- st::sendBoxAlbumGroupEditInternalSkip
- st::sendBoxAlbumGroupSkipRight;
const auto filepath = file.path;
if (filepath.isEmpty()) {
_name = filedialogDefaultName(
@@ -245,6 +359,45 @@ AlbumThumb::AlbumThumb(
_nameWidth = st::semiboldFont->width(_name);
}
_statusWidth = st::normalFont->width(_status);
_editMedia.create(parent, st::sendBoxAlbumGroupButtonFile);
_deleteMedia.create(parent, st::sendBoxAlbumGroupButtonFile);
const auto duration = st::historyAttach.ripple.hideDuration;
_editMedia->setClickedCallback(App::LambdaDelayed(
duration,
parent,
std::move(editCallback)));
_deleteMedia->setClickedCallback(App::LambdaDelayed(
duration,
parent,
std::move(deleteCallback)));
_editMedia->setIconOverride(&st::editMediaButtonIconFile);
_deleteMedia->setIconOverride(&st::sendBoxAlbumGroupDeleteButtonIconFile);
updateFileRow(-1);
}
void AlbumThumb::updateFileRow(int row) {
if (row < 0) {
_editMedia->hide();
_deleteMedia->hide();
return;
}
_editMedia->show();
_deleteMedia->show();
const auto fileHeight = st::sendMediaFileThumbSize
+ st::sendMediaFileThumbSkip;
const auto top = row * fileHeight + st::sendBoxAlbumGroupSkipTop;
const auto size = st::editMediaButtonSize;
auto right = st::sendBoxAlbumGroupSkipRight + size;
_deleteMedia->moveToRight(right, top);
right += st::sendBoxAlbumGroupEditInternalSkip + size;
_editMedia->moveToRight(right, top);
}
void AlbumThumb::resetLayoutAnimation() {
@@ -337,6 +490,12 @@ void AlbumThumb::paintInAlbum(
}
st::historyFileThumbPlay.paintInCenter(p, inner);
}
_lastRectOfButtons = PaintAlbumThumbButtons(
p,
{ x, y },
geometry.width(),
shrinkProgress);
}
void AlbumThumb::prepareCache(QSize size, int shrink) {
@@ -490,6 +649,12 @@ void AlbumThumb::paintPhoto(Painter &p, int left, int top, int outerWidth) {
top,
outerWidth,
_photo);
_lastRectOfButtons = PaintAlbumThumbButtons(
p,
{ left, top },
st::sendMediaPreviewSize,
0);
}
void AlbumThumb::paintFile(Painter &p, int left, int top, int outerWidth) {
@@ -520,6 +685,19 @@ bool AlbumThumb::containsPoint(QPoint position) const {
return _layout.geometry.contains(position);
}
bool AlbumThumb::buttonsContainPoint(QPoint position) const {
return _lastRectOfButtons.contains(position);
}
ButtonType AlbumThumb::buttonTypeFromPoint(QPoint position) const {
if (!buttonsContainPoint(position)) {
return ButtonType::None;
}
return (position.x() < _lastRectOfButtons.center().x())
? ButtonType::Edit
: ButtonType::Delete;
}
int AlbumThumb::distanceTo(QPoint position) const {
const auto delta = (_layout.geometry.center() - position);
return QPoint::dotProduct(delta, delta);
@@ -601,14 +779,12 @@ SingleMediaPreview *SingleMediaPreview::Create(
preview.height())) {
return nullptr;
}
const auto sticker = (file.information->filemime == kStickerMimeString)
|| (file.information->filemime == kAnimatedStickerMimeString);
return Ui::CreateChild<SingleMediaPreview>(
parent,
controller,
preview,
animated,
sticker,
Core::IsMimeSticker(file.information->filemime),
animationPreview ? file.path : QString());
}
@@ -960,6 +1136,14 @@ public:
void setSendWay(SendFilesWay way);
std::vector<int> takeOrder();
auto thumbDeleted() {
return _thumbDeleted.events();
}
auto thumbChanged() {
return _thumbChanged.events();
}
protected:
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
@@ -974,6 +1158,15 @@ private:
void prepareThumbs();
void updateSizeAnimated(const std::vector<Ui::GroupMediaLayout> &layout);
void updateSize();
void updateFileRows();
int thumbIndex(AlbumThumb *thumb);
AlbumThumb *thumbUnderCursor();
void deleteThumbByIndex(int index);
void changeThumbByIndex(int index);
void thumbButtonsCallback(
not_null<AlbumThumb*> thumb,
ButtonType type);
void paintAlbum(Painter &p) const;
void paintPhotos(Painter &p, QRect clip) const;
@@ -1003,6 +1196,9 @@ private:
AlbumThumb *_paintedAbove = nullptr;
QPoint _draggedStartPosition;
rpl::event_stream<int> _thumbDeleted;
rpl::event_stream<int> _thumbChanged;
mutable Ui::Animations::Simple _thumbsHeightAnimation;
mutable Ui::Animations::Simple _shrinkAnimation;
mutable Ui::Animations::Simple _finishDragAnimation;
@@ -1019,6 +1215,7 @@ SendFilesBox::AlbumPreview::AlbumPreview(
setMouseTracking(true);
prepareThumbs();
updateSize();
updateFileRows();
}
void SendFilesBox::AlbumPreview::setSendWay(SendFilesWay way) {
@@ -1027,9 +1224,18 @@ void SendFilesBox::AlbumPreview::setSendWay(SendFilesWay way) {
_sendWay = way;
}
updateSize();
updateFileRows();
update();
}
void SendFilesBox::AlbumPreview::updateFileRows() {
Expects(_order.size() == _thumbs.size());
const auto isFile = (_sendWay == SendFilesWay::Files);
for (auto i = 0; i < _order.size(); i++) {
_thumbs[i]->updateFileRow(isFile ? _order[i] : -1);
}
}
std::vector<int> SendFilesBox::AlbumPreview::takeOrder() {
auto reordered = std::vector<std::unique_ptr<AlbumThumb>>();
reordered.reserve(_thumbs.size());
@@ -1071,7 +1277,10 @@ void SendFilesBox::AlbumPreview::prepareThumbs() {
for (auto i = 0; i != count; ++i) {
_thumbs.push_back(std::make_unique<AlbumThumb>(
_list.files[i],
layout[i]));
layout[i],
this,
[=] { changeThumbByIndex(thumbIndex(thumbUnderCursor())); },
[=] { deleteThumbByIndex(thumbIndex(thumbUnderCursor())); }));
}
_thumbsHeight = countLayoutHeight(layout);
_photosHeight = ranges::accumulate(ranges::view::all(
@@ -1094,9 +1303,27 @@ int SendFilesBox::AlbumPreview::contentTop() const {
AlbumThumb *SendFilesBox::AlbumPreview::findThumb(QPoint position) const {
position -= QPoint(contentLeft(), contentTop());
const auto i = ranges::find_if(_thumbs, [&](const auto &thumb) {
return thumb->containsPoint(position);
});
auto top = 0;
const auto isPhotosWay = (_sendWay == SendFilesWay::Photos);
const auto skip = isPhotosWay
? st::sendMediaPreviewPhotoSkip
: st::sendMediaFileThumbSkip;
auto find = [&](const auto &thumb) {
if (_sendWay == SendFilesWay::Album) {
return thumb->containsPoint(position);
} else if (isPhotosWay || _sendWay == SendFilesWay::Files) {
const auto bottom = top + (isPhotosWay
? thumb->photoHeight()
: st::sendMediaFileThumbSize);
const auto isUnderTop = (position.y() > top);
top = bottom + skip;
return isUnderTop && (position.y() < bottom);
}
return false;
};
const auto i = ranges::find_if(_thumbs, std::move(find));
return (i == _thumbs.end()) ? nullptr : i->get();
}
@@ -1277,6 +1504,56 @@ void SendFilesBox::AlbumPreview::paintFiles(Painter &p, QRect clip) const {
}
}
int SendFilesBox::AlbumPreview::thumbIndex(AlbumThumb *thumb) {
if (!thumb) {
return -1;
}
const auto thumbIt = ranges::find_if(_thumbs, [&](auto &t) {
return t.get() == thumb;
});
Expects(thumbIt != _thumbs.end());
return std::distance(_thumbs.begin(), thumbIt);
}
AlbumThumb *SendFilesBox::AlbumPreview::thumbUnderCursor() {
return findThumb(mapFromGlobal(QCursor::pos()));
}
void SendFilesBox::AlbumPreview::deleteThumbByIndex(int index) {
if (index < 0) {
return;
}
const auto orderIt = ranges::find(_order, index);
Expects(orderIt != _order.end());
_order.erase(orderIt);
ranges::for_each(_order, [=](auto &i) {
if (i > index) {
i--;
}
});
_thumbDeleted.fire(std::move(index));
}
void SendFilesBox::AlbumPreview::changeThumbByIndex(int index) {
if (index < 0) {
return;
}
_thumbChanged.fire(std::move(index));
}
void SendFilesBox::AlbumPreview::thumbButtonsCallback(
not_null<AlbumThumb*> thumb,
ButtonType type) {
const auto index = thumbIndex(thumb);
switch (type) {
case ButtonType::None: return;
case ButtonType::Edit: changeThumbByIndex(index); break;
case ButtonType::Delete: deleteThumbByIndex(index); break;
}
}
void SendFilesBox::AlbumPreview::mousePressEvent(QMouseEvent *e) {
if (_finishDragAnimation.animating()) {
return;
@@ -1284,6 +1561,10 @@ void SendFilesBox::AlbumPreview::mousePressEvent(QMouseEvent *e) {
const auto position = e->pos();
cancelDrag();
if (const auto thumb = findThumb(position)) {
if (thumb->buttonsContainPoint(e->pos())) {
thumbButtonsCallback(thumb, thumb->buttonTypeFromPoint(e->pos()));
return;
}
_paintedAbove = _suggestedThumb = _draggedThumb = thumb;
_draggedStartPosition = position;
_shrinkAnimation.start([=] { update(); }, 0., 1., kShrinkDuration);
@@ -1291,19 +1572,26 @@ void SendFilesBox::AlbumPreview::mousePressEvent(QMouseEvent *e) {
}
void SendFilesBox::AlbumPreview::mouseMoveEvent(QMouseEvent *e) {
if (_sendWay != SendFilesWay::Album) {
if (_sendWay == SendFilesWay::Files) {
applyCursor(style::cur_default);
return;
}
if (_draggedThumb) {
const auto isAlbum = (_sendWay == SendFilesWay::Album);
if (isAlbum && _draggedThumb) {
const auto position = e->pos();
_draggedThumb->moveInAlbum(position - _draggedStartPosition);
updateSuggestedDrag(_draggedThumb->center());
update();
} else {
const auto cursor = findThumb(e->pos())
const auto thumb = findThumb(e->pos());
const auto regularCursor = isAlbum
? style::cur_sizeall
: style::cur_default;
const auto cursor = thumb
? (thumb->buttonsContainPoint(e->pos())
? style::cur_pointer
: regularCursor)
: style::cur_default;
applyCursor(cursor);
}
}
@@ -1405,7 +1693,8 @@ void SendFilesBox::initPreview(rpl::producer<int> desiredPreviewHeight) {
) | rpl::start_with_next([=](int height) {
setDimensions(
st::boxWideWidth,
std::min(st::sendMediaPreviewHeightMax, height));
std::min(st::sendMediaPreviewHeightMax, height),
true);
}, lifetime());
if (_preview) {
@@ -1414,7 +1703,7 @@ void SendFilesBox::initPreview(rpl::producer<int> desiredPreviewHeight) {
}
void SendFilesBox::prepareSingleFilePreview() {
Expects(_list.files.size() == 1);
Expects(IsSingleItem(_list));
const auto &file = _list.files[0];
const auto media = SingleMediaPreview::Create(this, _controller, file);
@@ -1442,11 +1731,74 @@ void SendFilesBox::prepareAlbumPreview() {
this,
_list,
_sendWay->value()));
addThumbButtonHandlers(wrap);
_preview = wrap;
_albumPreview->show();
setupShadows(wrap, _albumPreview);
initPreview(_albumPreview->desiredHeightValue());
crl::on_main([=] {
wrap->scrollToY(_lastScrollTop);
_lastScrollTop = 0;
});
}
void SendFilesBox::addThumbButtonHandlers(not_null<Ui::ScrollArea*> wrap) {
_albumPreview->thumbDeleted(
) | rpl::start_with_next([=](auto index) {
_lastScrollTop = wrap->scrollTop();
_list.files.erase(_list.files.begin() + index);
applyAlbumOrder();
if (_preview) {
_preview->deleteLater();
}
_albumPreview = nullptr;
if (IsSingleItem(_list)) {
_list.albumIsPossible = false;
if (_sendWay->value() == SendFilesWay::Album) {
_sendWay->setValue(SendFilesWay::Photos);
}
}
_compressConfirm = _compressConfirmInitial;
refreshAllAfterAlbumChanges();
}, _albumPreview->lifetime());
_albumPreview->thumbChanged(
) | rpl::start_with_next([=](auto index) {
_lastScrollTop = wrap->scrollTop();
const auto callback = [=](FileDialog::OpenResult &&result) {
FileDialogCallback(
std::move(result),
true,
[=] (auto list) {
_list.files[index] = std::move(list.files.front());
applyAlbumOrder();
if (_preview) {
_preview->deleteLater();
}
_albumPreview = nullptr;
refreshAllAfterAlbumChanges();
});
};
FileDialog::GetOpenPath(
this,
tr::lng_choose_file(tr::now),
FileDialog::AlbumFilesFilter(),
crl::guard(this, callback));
}, _albumPreview->lifetime());
}
void SendFilesBox::setupShadows(
@@ -1480,9 +1832,13 @@ void SendFilesBox::setupShadows(
}
void SendFilesBox::prepare() {
_send = addButton(tr::lng_send_button(), [=] { send({}); });
_send = addButton(
(_sendType == Api::SendType::Normal
? tr::lng_send_button()
: tr::lng_schedule_button()),
[=] { send({}); });
if (_sendType == Api::SendType::Normal) {
SetupSendMenu(
SetupSendMenuAndShortcuts(
_send,
[=] { return _sendMenuType; },
[=] { sendSilent(); },
@@ -1497,6 +1853,49 @@ void SendFilesBox::prepare() {
_cancelledCallback();
}
}, lifetime());
const auto title = tr::lng_stickers_featured_add(tr::now) + qsl("...");
_addFileToAlbum = addLeftButton(
rpl::single(title),
App::LambdaDelayed(st::historyAttach.ripple.hideDuration, this, [=] {
openDialogToAddFileToAlbum();
}));
updateLeftButtonVisibility();
}
void SendFilesBox::updateLeftButtonVisibility() {
const auto isAlbum = _list.albumIsPossible
&& (_list.files.size() < Storage::MaxAlbumItems());
if (isAlbum || (IsSingleItem(_list) && IsFirstAlbumItem(_list))) {
_addFileToAlbum->show();
} else {
_addFileToAlbum->hide();
}
}
void SendFilesBox::refreshAllAfterAlbumChanges() {
refreshAlbumMediaCount();
preparePreview();
captionResized();
updateLeftButtonVisibility();
}
void SendFilesBox::openDialogToAddFileToAlbum() {
const auto callback = [=](FileDialog::OpenResult &&result) {
FileDialogCallback(
std::move(result),
true,
[=] (auto list) {
addFiles(std::move(list));
});
};
FileDialog::GetOpenPaths(
this,
tr::lng_choose_file(tr::now),
FileDialog::AlbumFilesFilter(),
crl::guard(this, callback));
}
void SendFilesBox::initSendWay() {
@@ -1574,7 +1973,7 @@ void SendFilesBox::refreshAlbumMediaCount() {
}
void SendFilesBox::preparePreview() {
if (_list.files.size() == 1) {
if (IsSingleItem(_list)) {
prepareSingleFilePreview();
} else {
if (_list.albumIsPossible) {
@@ -1611,7 +2010,7 @@ void SendFilesBox::setupSendWayControls() {
addRadio(_sendAlbum, SendFilesWay::Album, tr::lng_send_album(tr::now));
}
if (!_list.albumIsPossible || _albumPhotosCount > 0) {
addRadio(_sendPhotos, SendFilesWay::Photos, (_list.files.size() == 1)
addRadio(_sendPhotos, SendFilesWay::Photos, IsSingleItem(_list)
? tr::lng_send_photo(tr::now)
: (_albumVideosCount > 0)
? tr::lng_send_separate_photos_videos(tr::now)
@@ -1619,7 +2018,7 @@ void SendFilesBox::setupSendWayControls() {
? tr::lng_send_separate_photos(tr::now)
: tr::lng_send_photos(tr::now, lt_count, _list.files.size())));
}
addRadio(_sendFiles, SendFilesWay::Files, (_list.files.size() == 1)
addRadio(_sendFiles, SendFilesWay::Files, (IsSingleItem(_list))
? tr::lng_send_file(tr::now)
: tr::lng_send_files(tr::now, lt_count, _list.files.size()));
}
@@ -1647,7 +2046,7 @@ void SendFilesBox::applyAlbumOrder() {
void SendFilesBox::setupCaption() {
_caption->setMaxLength(Global::CaptionLengthMax());
_caption->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
_caption->setSubmitSettings(_controller->session().settings().sendSubmitWay());
connect(_caption, &Ui::InputField::resized, [=] {
captionResized();
});
@@ -1746,16 +2145,9 @@ void SendFilesBox::captionResized() {
update();
}
bool SendFilesBox::canAddUrls(const QList<QUrl> &urls) const {
return !urls.isEmpty() && ranges::find_if(
urls,
[](const QUrl &url) { return !url.isLocalFile(); }
) == urls.end();
}
bool SendFilesBox::canAddFiles(not_null<const QMimeData*> data) const {
const auto urls = data->hasUrls() ? data->urls() : QList<QUrl>();
auto filesCount = canAddUrls(urls) ? urls.size() : 0;
auto filesCount = CanAddUrls(urls) ? urls.size() : 0;
if (!filesCount && data->hasImage()) {
++filesCount;
}
@@ -1764,8 +2156,7 @@ bool SendFilesBox::canAddFiles(not_null<const QMimeData*> data) const {
return false;
} else if (_list.files.size() > 1 && !_albumPreview) {
return false;
} else if (_list.files.front().type
== Storage::PreparedFile::AlbumType::None) {
} else if (!IsFirstAlbumItem(_list)) {
return false;
}
return true;
@@ -1774,7 +2165,7 @@ bool SendFilesBox::canAddFiles(not_null<const QMimeData*> data) const {
bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
auto list = [&] {
const auto urls = data->hasUrls() ? data->urls() : QList<QUrl>();
auto result = canAddUrls(urls)
auto result = CanAddUrls(urls)
? Storage::PrepareMediaList(urls, st::sendMediaPreviewSize)
: Storage::PreparedList(
Storage::PreparedList::Error::EmptyFile,
@@ -1792,35 +2183,35 @@ bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
}
return result;
}();
if (_list.files.size() + list.files.size() > Storage::MaxAlbumItems()) {
return addFiles(std::move(list));
}
bool SendFilesBox::addFiles(Storage::PreparedList list) {
const auto sumFiles = _list.files.size() + list.files.size();
const auto cutToAlbumSize = (sumFiles > Storage::MaxAlbumItems());
if (list.error != Storage::PreparedList::Error::None) {
return false;
} else if (list.error != Storage::PreparedList::Error::None) {
} else if (!IsSingleItem(list) && !list.albumIsPossible) {
return false;
} else if (list.files.size() != 1 && !list.albumIsPossible) {
return false;
} else if (list.files.front().type
== Storage::PreparedFile::AlbumType::None) {
} else if (!IsFirstAlbumItem(list)) {
return false;
} else if (_list.files.size() > 1 && !_albumPreview) {
return false;
} else if (_list.files.front().type
== Storage::PreparedFile::AlbumType::None) {
} else if (!IsFirstAlbumItem(_list)) {
return false;
}
applyAlbumOrder();
delete base::take(_preview);
_albumPreview = nullptr;
if (_list.files.size() == 1
if (IsSingleItem(_list)
&& _sendWay->value() == SendFilesWay::Photos) {
_sendWay->setValue(SendFilesWay::Album);
}
_list.mergeToEnd(std::move(list));
_list.mergeToEnd(std::move(list), cutToAlbumSize);
_compressConfirm = _compressConfirmInitial;
refreshAlbumMediaCount();
preparePreview();
captionResized();
refreshAllAfterAlbumChanges();
return true;
}
@@ -1858,7 +2249,9 @@ void SendFilesBox::updateBoxSize() {
}
void SendFilesBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
if (e->matches(QKeySequence::Open) && !_addFileToAlbum->isHidden()) {
openDialogToAddFileToAlbum();
} else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
const auto modifiers = e->modifiers();
const auto ctrl = modifiers.testFlag(Qt::ControlModifier)
|| modifiers.testFlag(Qt::MetaModifier);
@@ -1937,7 +2330,9 @@ void SendFilesBox::setInnerFocus() {
void SendFilesBox::send(
Api::SendOptions options,
bool ctrlShiftEnter) {
if (_sendType == Api::SendType::Scheduled && !options.scheduled) {
if ((_sendType == Api::SendType::Scheduled
|| _sendType == Api::SendType::ScheduledToUser)
&& !options.scheduled) {
return sendScheduled();
}
@@ -1982,9 +2377,12 @@ void SendFilesBox::sendSilent() {
}
void SendFilesBox::sendScheduled() {
const auto type = (_sendType == Api::SendType::ScheduledToUser)
? SendMenuType::ScheduledToUser
: _sendMenuType;
const auto callback = [=](Api::SendOptions options) { send(options); };
Ui::show(
HistoryView::PrepareScheduleBox(this, _sendMenuType, callback),
HistoryView::PrepareScheduleBox(this, type, callback),
Ui::LayerOption::KeepOther);
}

View File

@@ -120,9 +120,15 @@ private:
void updateControlsGeometry();
void updateCaptionPlaceholder();
void addThumbButtonHandlers(not_null<Ui::ScrollArea*> wrap);
bool canAddFiles(not_null<const QMimeData*> data) const;
bool canAddUrls(const QList<QUrl> &urls) const;
bool addFiles(not_null<const QMimeData*> data);
bool addFiles(Storage::PreparedList list);
void openDialogToAddFileToAlbum();
void updateLeftButtonVisibility();
void refreshAllAfterAlbumChanges();
const not_null<Window::SessionController*> _controller;
const Api::SendType _sendType = Api::SendType();
@@ -163,6 +169,9 @@ private:
int _albumVideosCount = 0;
int _albumPhotosCount = 0;
int _lastScrollTop = 0;
QPointer<Ui::RoundButton> _send;
QPointer<Ui::RoundButton> _addFileToAlbum;
};

View File

@@ -206,6 +206,7 @@ void ShareBox::prepareCommentField() {
field->setMarkdownReplacesEnabled(rpl::single(true));
field->setEditLinkCallback(
DefaultEditLinkCallback(&_navigation->session(), field));
field->setSubmitSettings(_navigation->session().settings().sendSubmitWay());
InitSpellchecker(&_navigation->session(), field);
Ui::SendPendingMoveResizeEvents(_comment);
@@ -413,7 +414,9 @@ void ShareBox::keyPressEvent(QKeyEvent *e) {
SendMenuType ShareBox::sendMenuType() const {
const auto selected = _inner->selected();
return (selected.size() == 1 && selected.front()->isSelf())
return ranges::all_of(selected, HistoryView::CanScheduleUntilOnline)
? SendMenuType::ScheduledToUser
: (selected.size() == 1 && selected.front()->isSelf())
? SendMenuType::Reminder
: SendMenuType::Scheduled;
}
@@ -424,7 +427,7 @@ void ShareBox::createButtons() {
const auto send = addButton(tr::lng_share_confirm(), [=] {
submit({});
});
SetupSendMenu(
SetupSendMenuAndShortcuts(
send,
[=] { return sendMenuType(); },
[=] { submitSilent(); },

View File

@@ -44,7 +44,7 @@ private:
MTP::Sender _api;
MsgId _offsetId = 0;
mtpRequestId _loadRequestId = 0;
int _loadRequestId = 0; // Not a real mtpRequestId.
bool _allLoaded = false;
};

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