Compare commits

...

158 Commits

Author SHA1 Message Date
John Preston
0f28069881 Beta version 2.9.13: Fix clean build. 2021-08-31 13:35:12 +03:00
John Preston
cbe38af427 Beta version 2.9.13: Mark 'isPrefix' as '[[maybe_unused]]'. 2021-08-31 12:45:37 +03:00
John Preston
4598ce2671 Beta version 2.9.13: Update lib_webview. 2021-08-31 11:13:48 +03:00
John Preston
244ccba871 Beta version 2.9.13.
- See unread comments count when scrolling discussions in channels.
2021-08-31 10:37:31 +03:00
23rd
dc8eb79295 Moved code of data send actions from Data::Session to separated file. 2021-08-30 23:08:57 +03:00
23rd
f7abd85761 Slightly optimized replacement in send action text. 2021-08-30 21:45:56 +03:00
23rd
436212bb88 Added animation of send action for stickers to middle of text. 2021-08-30 20:50:36 +03:00
23rd
8c17e3e578 Added sending action when choosing sticker. 2021-08-30 20:50:32 +03:00
23rd
0852dbc40f Formatted classes for send actions. 2021-08-30 20:50:28 +03:00
23rd
ccea6ce492 Added animated indicator of choosing sticker action to chats list. 2021-08-30 20:50:24 +03:00
John Preston
2d0bcf7dca Resend non-authorization requests on main DC change. 2021-08-30 20:32:58 +03:00
John Preston
ae40ea9336 Always start with QR-code login. 2021-08-30 20:32:58 +03:00
John Preston
52a6282eb9 Count unread replies locally when possible. 2021-08-30 20:32:58 +03:00
John Preston
c39024c7fd Track and display unread count in discussions. 2021-08-30 20:32:58 +03:00
John Preston
85e4c8527b Always write local drafts the same way. 2021-08-30 20:32:58 +03:00
John Preston
f2da34c9f5 Fix jump to message / to end in discussions. 2021-08-30 20:32:58 +03:00
John Preston
9709297713 Remove unread bar in not-seen post comments opening. 2021-08-30 20:32:58 +03:00
John Preston
809b0e9fe0 Fix build for Linux. 2021-08-30 20:32:58 +03:00
John Preston
d684c8057c Fix build for macOS. 2021-08-30 20:32:57 +03:00
John Preston
d5820e7a5a Closed alpha version 2.9.12.1. 2021-08-30 20:32:57 +03:00
John Preston
beff635e45 Colorize bubbles according to a custom chat theme. 2021-08-30 20:32:57 +03:00
John Preston
5de83ef30c Fix assertion violation in profile video with zero file size. 2021-08-30 20:32:57 +03:00
John Preston
f5a323e40a Move all background helper methods to chat_theme module. 2021-08-30 20:32:57 +03:00
John Preston
0a1e84ddb2 Move ChatTheme to td_ui. 2021-08-30 20:32:57 +03:00
John Preston
3cd0f9d189 Start non-unique ChatTheme. 2021-08-30 20:32:57 +03:00
John Preston
f3dd8c68b3 Load chat cloud themes list. 2021-08-30 20:32:57 +03:00
John Preston
70808dfa7d Show chat theme changing service messages. 2021-08-30 20:32:57 +03:00
John Preston
0821c21285 Support mono playback in OpenAL ADM. 2021-08-30 20:32:57 +03:00
John Preston
29c0956d61 Always try to play video in voice chats. 2021-08-30 20:32:57 +03:00
John Preston
0cfede984c Update API scheme to layer 132. 2021-08-30 20:32:56 +03:00
23rd
558e1d96fd Simplified extracting of calling code from phone number. 2021-08-30 20:32:56 +03:00
23rd
44c188024e Fixed updating of masked input field in intro. 2021-08-30 20:32:56 +03:00
23rd
3acbcc6247 Updated fallback countries list. 2021-08-30 20:32:56 +03:00
23rd
6caddb5141 Removed old code entry from country info. 2021-08-30 20:32:56 +03:00
23rd
963fda69a8 Removed old formatting of phone numbers. 2021-08-30 20:32:56 +03:00
23rd
6ff0cb853d Added ability to extract pattern groups from incomplete phone number. 2021-08-29 23:31:12 +03:00
23rd
10c8162575 Added ability to extract pattern groups from phone number in modern way. 2021-08-29 23:31:12 +03:00
23rd
e4640590d0 Added ability to format phone numbers in modern way. 2021-08-29 23:31:12 +03:00
23rd
e5b85bbaf1 Improved country caching. 2021-08-29 23:31:12 +03:00
23rd
8310d7e47c Improved parsing of countries data from server. 2021-08-29 23:31:12 +03:00
23rd
cf523953ad Added initial manager of countries. 2021-08-29 23:29:39 +03:00
23rd
3e80c04da7 Replaced const char ptr with QString in country structs. 2021-08-29 21:01:30 +03:00
23rd
c593f43629 Made list of countries mutable. 2021-08-29 21:01:30 +03:00
23rd
86aaa9673d Moved countries to singleton. 2021-08-29 21:01:10 +03:00
23rd
a230e83778 Simplified some names in countries. 2021-08-29 21:00:48 +03:00
23rd
df02bbb0a3 Moved countries from Data to Countries namespace. 2021-08-29 20:39:43 +03:00
John Preston
1b3075ac2e Fix pattern background tiling on Retina screens. 2021-08-25 17:15:07 +03:00
John Preston
d45c530db0 Fix CDN downloads after authorization. 2021-08-25 16:24:20 +03:00
John Preston
2671e67119 Fix crash on cancelled Qr login attempt. 2021-08-25 16:24:15 +03:00
John Preston
bdc275a927 Don't crash on bad userId in local storage. 2021-08-25 16:24:09 +03:00
Ilya Fedin
6192413f0b Split webview initialization from GtkIntegration class 2021-08-25 12:52:43 +03:00
Ilya Fedin
51df482571 Get rid of gtk open with dialog
Portal open with dialog works just fine and is a more universal solution... That allows to get rid of an additional process.
2021-08-25 12:52:43 +03:00
John Preston
2694cb76a7 Remove crash debug information from streaming. 2021-08-25 11:24:32 +03:00
John Preston
b5ae492f5e Beta version 2.9.12: Fix build on Windows. 2021-08-24 21:08:35 +03:00
John Preston
018ee0564f Beta version 2.9.12.
- Disable floating point exceptions in 32 bit Windows version.
2021-08-24 20:34:54 +03:00
John Preston
156eb69d38 Disable fp exceptions in Windows 32 bit build. 2021-08-24 20:32:26 +03:00
23rd
df0229cffd Fixed render of text selection in sections.
Regression was introduced in f4fdadd3b0.
2021-08-24 20:02:36 +03:00
23rd
b3f8d0e81a Removed dcId from passport FileKey. 2021-08-24 20:02:36 +03:00
23rd
559d488b0b Moved load status of files in passport to separated class. 2021-08-24 20:02:36 +03:00
John Preston
401529e7d1 Fix possible crash in media viewer hide workaround. 2021-08-24 19:25:31 +03:00
John Preston
a6fb0e372e Beta version 2.9.11.
- Resolve (again) a video playback crash in 32 bit Windows version.
2021-08-24 18:12:26 +03:00
John Preston
7948fc509e Remove (incorrect) checks for double casts. 2021-08-24 18:11:47 +03:00
John Preston
2d6155fc85 Throw fp exceptions in Windows 32 bit build. 2021-08-24 17:43:31 +03:00
John Preston
c7e60ef723 Beta version 2.9.10.
- Resolve (hopefully) a video playback crash in 32 bit Windows version.
2021-08-24 12:42:58 +03:00
John Preston
8f5830d520 Workaround both std::round-s in video streaming. 2021-08-24 12:42:27 +03:00
John Preston
f21d7821e7 Beta version 2.9.9.
- Still(3) debugging a video playback crash in 32 bit Windows version.
2021-08-23 20:34:38 +03:00
John Preston
e8f1373edc Add some checks for NAN in video playback. 2021-08-23 20:29:40 +03:00
John Preston
c8d1e01159 Beta version 2.9.8.
- And still debugging a video playback crash in 32 bit Windows version.
2021-08-23 18:30:14 +03:00
John Preston
7e6f24552a Add basic OpenGL info to crash annotations. 2021-08-23 18:29:20 +03:00
John Preston
27d58ba07b Try to do a non-failed double->crl::time cast.
A crash on some old CPUs show, that in video frame processing
sometimes a cast from double to crl::time fails, writing to
the resulting crl::time value INT64_MIN (0x8000000000000000).

This is shown in crash logs, with lines like:

...,rounded:104,casted:-9223372036854775808,...

where logs are written like:

...
).arg(std::round(adjust * _options.speed)
).arg(crl::time(std::round(adjust * _options.speed))
...

I don't know what to do and how to workaround this. Trying other casts.
2021-08-23 17:58:59 +03:00
23rd
3a92a181a1 Fixed editing on Up arrow in sections with non-empty input field. 2021-08-23 13:57:35 +03:00
23rd
ddd5617043 Fixed processing GIF images as non-album files.
Fixed #16844.
2021-08-23 13:57:31 +03:00
John Preston
70b3e414ce Fix crash in Update-requested-by-tg://-link. 2021-08-23 13:54:49 +03:00
John Preston
2b04653f24 Beta version 2.9.7.
- Still debugging a video playback crash in 32 bit Windows version.
2021-08-23 12:21:09 +03:00
John Preston
868015da25 Add extensive video playback crash logs. 2021-08-23 12:17:40 +03:00
John Preston
7aeffa242e Beta version 2.9.6.
- Debugging a video playback crash in 32 bit Windows version.
2021-08-21 09:34:26 +03:00
John Preston
3136c0586e Add some more assertions to debug a crash. 2021-08-21 09:33:20 +03:00
John Preston
fb0fcbca7f Remove redundant -static-libstdc++ for Updater. 2021-08-20 21:14:39 +03:00
John Preston
0d449c037b Beta version 2.9.5: Fix hide workaround for media viewer. 2021-08-20 19:56:40 +03:00
John Preston
4d98230694 Beta version 2.9.5.
- Tile chat background patterns horizontally.
- Fix a rare crash in spellchecker on Windows.
- Fix animated chat backgrounds in Saved Messages.
- Fix "Sorry, group is inaccessible" message in scheduled voice chats.
2021-08-20 18:42:13 +03:00
John Preston
3d36e501a1 Add some assertions to debug a crash in video playback. 2021-08-20 18:42:13 +03:00
John Preston
b4eb9a0827 Fix single-colored patterns. 2021-08-20 16:24:26 +03:00
John Preston
aaf0015be4 Improve pattern wallpaper preview in Settings. 2021-08-20 16:14:09 +03:00
John Preston
1e8e163bb1 Tile patterns horizontally. 2021-08-20 15:55:10 +03:00
John Preston
f3f741e1eb Hide 'Tile' option in generated backgrounds. 2021-08-20 15:14:28 +03:00
John Preston
44f52ca6cd Fix solid color background preview. 2021-08-20 14:54:11 +03:00
John Preston
2b6e04bca3 Apply initial bubble opacity in QImage bubble backgrounds. 2021-08-20 14:54:11 +03:00
John Preston
784d57a2bc Use QImage bubbles background for some Media parts. 2021-08-20 14:54:11 +03:00
John Preston
f4fdadd3b0 Allow arbitrary QImage as outgoing bubbles background. 2021-08-20 14:54:11 +03:00
John Preston
1cc9a52461 Fix my speaking status freeze in voice chats. 2021-08-18 17:52:23 +03:00
John Preston
cd52982752 Hopefully fix an assertion violation in voice chats. 2021-08-18 17:45:21 +03:00
John Preston
4dd58b79e9 Fix forward of a bot message with a game from a group. 2021-08-18 16:43:22 +03:00
John Preston
c5c94276c2 Allow editing caption of forwarded audio file.
Fixes https://bugs.telegram.org/c/3025
2021-08-18 16:29:05 +03:00
John Preston
8e0b9b685c Show 'X subscribers' in channel ConfirmInviteBox.
Fixes https://bugs.telegram.org/c/2059
2021-08-18 15:57:52 +03:00
John Preston
1792bed721 Remove additional padding in shared contacts. 2021-08-18 15:52:04 +03:00
John Preston
a502cbc06e Move message link up in registered shared contacts.
Fixes #16804.
2021-08-18 15:48:30 +03:00
John Preston
ddda7b8c52 Fix crash in application Dock menu on macOS. 2021-08-18 15:44:38 +03:00
John Preston
0f19ba3231 Fix spellcheck crash on Windows on certain strings. 2021-08-18 15:22:27 +03:00
John Preston
36486a3d73 Fix creating a theme from a default one.
Regression was introduced in 79cc797aff.
2021-08-18 14:52:57 +03:00
John Preston
02f48a7781 Don't allow selected items together with text. 2021-08-18 14:52:49 +03:00
John Preston
c77f4dd794 Fix build on Linux. 2021-08-18 13:06:47 +03:00
John Preston
ca31a08182 Fix 'Sorry, group is inac...' in scheduled voice chat. 2021-08-18 11:34:16 +03:00
John Preston
33936195a1 Animate the chat background in Saved Messages. 2021-08-18 11:33:37 +03:00
John Preston
67bafa02fe Remove attempt of a shadow Qt build. 2021-08-17 18:15:55 +03:00
John Preston
1ef0046002 Beta version 2.9.4.
- Choose one from dozens of new gorgeous animated backgrounds
in Chat Settings > Chat background.
2021-08-17 18:09:18 +03:00
John Preston
f2f19b14eb Fix reading one message if last one is outgoing. 2021-08-17 18:07:20 +03:00
John Preston
662966ba31 Support patterns with negative intensity. 2021-08-17 17:35:10 +03:00
John Preston
5383ae3d96 Fix log in after password change by email. 2021-08-17 15:51:36 +03:00
John Preston
d80b25944e Fix Snap build on GitHub Actions. 2021-08-17 15:17:58 +03:00
23rd
76813db3ad Completed ability to recover cloud password with email. 2021-08-17 15:08:57 +03:00
John Preston
c3595f2e31 Ask for a new password when recovering by email. 2021-08-17 15:07:58 +03:00
John Preston
5a882d1fdc Rotate background in ListWidget. 2021-08-17 14:55:40 +03:00
John Preston
ce6f9f580f Remove unused ColorizePattern function. 2021-08-17 13:06:28 +03:00
John Preston
52b9a1fceb Log all Qt messages as usual in debug builds. 2021-08-17 12:25:13 +03:00
John Preston
1209b2692a Generate correct next rotated gradient. 2021-08-16 17:15:02 +03:00
John Preston
2abcb51dda Filter out patterns without background colors. 2021-08-16 17:14:34 +03:00
John Preston
7a06eccaec Make complex gradients animate on outgoing messages. 2021-08-16 16:07:43 +03:00
John Preston
a1f81e4de8 Generate static complex gradients. 2021-08-16 13:24:15 +03:00
John Preston
689378ee04 Prefer 1280x720 resolution for camera video. 2021-08-16 10:26:08 +03:00
John Preston
b239506150 Fix pattern wallpaper preview on retina screens. 2021-08-16 10:26:08 +03:00
John Preston
3dadcd9352 Animated transition on pattern-on-gradient resize. 2021-08-16 10:26:08 +03:00
John Preston
b9a9520ef5 Don't blend SoftLight patterns in realtime. 2021-08-16 10:26:08 +03:00
John Preston
2b46f87d7b Cache background quickly if no buttons pressed. 2021-08-16 10:26:08 +03:00
John Preston
2667bb3568 Move background caching to Window::SessionController. 2021-08-16 10:26:08 +03:00
John Preston
1bc5277d51 Show color / gradient wallpapers in WebPage previews. 2021-08-16 10:26:08 +03:00
John Preston
436d7b9d82 Add support for linear gradients without patterns. 2021-08-16 10:26:08 +03:00
John Preston
ba7e976fe2 Fix background sample generation. 2021-08-16 10:26:08 +03:00
John Preston
c2b1187948 Start support of linear gradient wallpapers. 2021-08-16 10:26:08 +03:00
23rd
1fd28d5cfb Removed MTP* from public interface of Api::CloudPassword. 2021-08-15 13:44:43 +03:00
23rd
eec58137e9 Fixed build for Linux. 2021-08-15 13:26:43 +03:00
Ilya Fedin
e7d39e6046 Get rid of GtkIntegration::initializeSettings use 2021-08-13 15:21:19 +03:00
Ilya Fedin
63a92cb90a Log icon theme 2021-08-13 15:21:19 +03:00
John Preston
85cc3b30a0 Don't use MTP* for WallPaper flags. 2021-08-12 12:32:30 +03:00
John Preston
474a6a71d9 Move unread mentions menu to chat_helpers/send_context_menu. 2021-08-12 10:06:16 +03:00
John Preston
393173c1da Finish 'Mark mentions as read' context menu. 2021-08-12 09:46:02 +03:00
John Preston
badc27eda4 Fix build with a correct lib_ui revision. 2021-08-12 09:03:19 +03:00
test
920f3b245b Update lang.strings 2021-08-12 08:52:56 +03:00
test
f189ffc6ac Summary 2021-08-12 08:52:56 +03:00
Nicholas Guriev
840bb447ba Mention some missing includes in payments/ subdirectory 2021-08-12 08:51:45 +03:00
Ilya Fedin
414456d003 Revert "Use gtk clipboard when available to avoid https://bugreports.qt.io/browse/QTBUG-56595"
Fixed in Qt by https://codereview.qt-project.org/c/qt/qtbase/+/306771

This reverts commit 3a91003eea.
2021-08-12 08:51:10 +03:00
23rd
ea3191badf Fixed Github CI build. 2021-08-12 08:34:51 +03:00
John Preston
4452edcaad Update release asset uploading script. 2021-08-12 08:34:47 +03:00
23rd
76eb00cea9 Added drafts saving on account switching. 2021-08-12 02:36:41 +03:00
23rd
b3622b413e Added ability to set custom auto-lock timer. 2021-08-12 02:36:41 +03:00
23rd
b55383efe7 Moved time input widgets to lib_ui. 2021-08-12 02:36:41 +03:00
23rd
852e46f0c9 Added filepath removal for modified images in photo editor.
Fixed #16791.
2021-08-12 02:35:53 +03:00
John Preston
84fef1f045 Fix linking to QtSvg properly. 2021-08-11 20:22:45 +03:00
John Preston
6cadf54874 Add support for SVG patterns in wallpapers. 2021-08-11 19:56:12 +03:00
John Preston
e8fc874456 Build and link with QtSvg. 2021-08-11 19:55:47 +03:00
John Preston
c79cd0b692 Use Images::Read instead of App::readImage. 2021-08-11 18:55:08 +03:00
John Preston
b150ab8ef5 Add .tgv as a known mime type. 2021-08-11 18:23:14 +03:00
Ilya Fedin
8b7b0fa570 Remove -externalupdater flag
Having a path to executable in /etc/tdesktop/externalupdater is a way more convenient and is enough
2021-08-11 18:20:52 +03:00
Ilya Fedin
a3ee1e4ed5 Remove mentions of unused -testmode flag 2021-08-11 18:20:52 +03:00
Ilya Fedin
349446e6b0 Lock issues once a day rather than once a hour 2021-08-11 17:56:25 +03:00
Ilya Fedin
97262a99c7 Get rid of osx and linux32 special targets 2021-08-11 16:59:27 +03:00
Ilya Fedin
1d2e34f5e9 Write Qt messages only to log in debug mode 2021-08-11 15:49:31 +03:00
Ilya Fedin
bc2fc94e25 Don't check libtgvoip defines, too big queue 2021-08-11 15:46:38 +03:00
Ilya Fedin
6f0e94a04a WebKitGTK support doesn't depend on GTK integration anymore 2021-08-11 15:46:38 +03:00
344 changed files with 7789 additions and 5320 deletions

View File

@@ -63,8 +63,6 @@ jobs:
- "DESKTOP_APP_DISABLE_X11_INTEGRATION"
- "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION"
- "DESKTOP_APP_DISABLE_GTK_INTEGRATION"
- "LIBTGVOIP_DISABLE_ALSA"
- "LIBTGVOIP_DISABLE_PULSEAUDIO"
env:
UPLOAD_ARTIFACT: "false"
@@ -94,9 +92,6 @@ jobs:
if [ "${{ matrix.defines }}" == "DESKTOP_APP_DISABLE_DBUS_INTEGRATION" ]; then
DEFINE="$DEFINE -D DESKTOP_APP_DISABLE_GTK_INTEGRATION=ON -D DESKTOP_APP_DISABLE_WEBKITGTK=ON"
fi
if [ "${{ matrix.defines }}" == "DESKTOP_APP_DISABLE_GTK_INTEGRATION" ]; then
DEFINE="$DEFINE -D DESKTOP_APP_DISABLE_WEBKITGTK=ON"
fi
echo Define from matrix: $DEFINE
echo "ARTIFACT_NAME=Telegram_${{ matrix.defines }}" >> $GITHUB_ENV
else

View File

@@ -2,7 +2,7 @@ name: 'Lock Threads'
on:
schedule:
- cron: '0 * * * *'
- cron: '0 0 * * *'
jobs:
lock:

View File

@@ -449,10 +449,11 @@ jobs:
git clone git://code.qt.io/qt/qt5.git qt_$QT
cd qt_$QT
perl init-repository --module-subset=qtbase,qtimageformats
perl init-repository --module-subset=qtbase,qtimageformats,qtsvg
git checkout v5.15.2
git submodule update qtbase
git submodule update qtimageformats
git submodule update qtsvg
cd qtbase
find ../../patches/qtbase_$QT -type f -print0 | sort -z | xargs -0 git apply
cd ..

View File

@@ -343,10 +343,11 @@ jobs:
run: |
git clone git://code.qt.io/qt/qt5.git qt_%QT%
cd qt_%QT%
perl init-repository --module-subset=qtbase,qtimageformats
perl init-repository --module-subset=qtbase,qtimageformats,qtsvg
git checkout v%QT_VER%
git submodule update qtbase
git submodule update qtimageformats
git submodule update qtsvg
cd qtbase
for /r %%i in (..\..\patches\qtbase_%QT%\*) do git apply %%i
cd ..

View File

@@ -345,6 +345,8 @@ PRIVATE
core/utils.cpp
core/utils.h
core/version.h
countries/countries_manager.cpp
countries/countries_manager.h
data/stickers/data_stickers_set.cpp
data/stickers/data_stickers_set.h
data/stickers/data_stickers.cpp
@@ -420,6 +422,8 @@ PRIVATE
data/data_reply_preview.h
data/data_search_controller.cpp
data/data_search_controller.h
data/data_send_action.cpp
data/data_send_action.h
data/data_session.cpp
data/data_session.h
data/data_scheduled_messages.cpp
@@ -854,14 +858,9 @@ PRIVATE
payments/payments_form.h
platform/linux/linux_desktop_environment.cpp
platform/linux/linux_desktop_environment.h
platform/linux/linux_gdk_helper.cpp
platform/linux/linux_gdk_helper.h
platform/linux/linux_gtk_integration_dummy.cpp
platform/linux/linux_gtk_integration_p.h
platform/linux/linux_gtk_integration.cpp
platform/linux/linux_gtk_integration.h
platform/linux/linux_gtk_open_with_dialog.cpp
platform/linux/linux_gtk_open_with_dialog.h
platform/linux/linux_wayland_integration_dummy.cpp
platform/linux/linux_wayland_integration.cpp
platform/linux/linux_wayland_integration.h
@@ -1166,12 +1165,7 @@ endif()
if (DESKTOP_APP_DISABLE_GTK_INTEGRATION)
remove_target_sources(Telegram ${src_loc}
platform/linux/linux_gdk_helper.cpp
platform/linux/linux_gdk_helper.h
platform/linux/linux_gtk_integration_p.h
platform/linux/linux_gtk_integration.cpp
platform/linux/linux_gtk_open_with_dialog.cpp
platform/linux/linux_gtk_open_with_dialog.h
)
else()
remove_target_sources(Telegram ${src_loc}
@@ -1288,19 +1282,6 @@ else()
desktop-app::external_kwayland
)
endif()
if (NOT DESKTOP_APP_DISABLE_GTK_INTEGRATION)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED gtk+-3.0)
target_include_directories(Telegram SYSTEM PRIVATE ${GTK_INCLUDE_DIRS})
if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
target_link_libraries(Telegram PRIVATE X11)
endif()
target_link_libraries(Telegram PRIVATE rt)
endif()
endif()
if (build_macstore)
@@ -1317,10 +1298,6 @@ if (build_macstore)
COMMAND mkdir -p $<TARGET_FILE_DIR:Telegram>/../Frameworks
COMMAND cp -a ${libs_loc}/breakpad/src/client/mac/build/Release/Breakpad.framework $<TARGET_FILE_DIR:Telegram>/../Frameworks/Breakpad.framework
)
elseif (build_osx)
set(bundle_identifier "com.tdesktop.Telegram$<$<CONFIG:Debug>:DebugOsx>")
set(bundle_entitlements "Telegram.entitlements")
set(output_name "Telegram")
else()
set(bundle_identifier "com.tdesktop.Telegram$<$<CONFIG:Debug>:Debug>")
set(bundle_entitlements "Telegram.entitlements")
@@ -1438,10 +1415,6 @@ if ((NOT DESKTOP_APP_DISABLE_AUTOUPDATE OR APPLE) AND NOT build_macstore AND NOT
endif()
endif()
if (LINUX)
target_link_options(Updater PRIVATE -static-libstdc++)
endif()
if (DESKTOP_APP_SPECIAL_TARGET)
add_executable(Packer)
init_target(Packer)

View File

@@ -577,6 +577,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_passcode_autolock_minutes#other" = "{count} minutes";
"lng_passcode_autolock_hours#one" = "{count} hour";
"lng_passcode_autolock_hours#other" = "{count} hours";
"lng_passcode_autolock_hours_minutes" = "{hours_count}h {minutes_count}m";
"lng_passcode_enter_old" = "Enter current passcode";
"lng_passcode_enter_first" = "Enter a passcode";
"lng_passcode_enter_new" = "Enter new passcode";
@@ -1208,6 +1209,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_proximity_reached" = "{from} is now within {distance} from {user}";
"lng_action_proximity_reached_you" = "{from} is now within {distance} from you";
"lng_action_you_proximity_reached" = "You are now within {distance} from {user}";
"lng_action_you_theme_changed" = "You changed chat theme to {emoji}";
"lng_action_theme_changed" = "{from} changed chat theme to {emoji}";
"lng_action_theme_changed_channel" = "Channel theme changed to {emoji}";
"lng_action_you_theme_disabled" = "You disabled chat theme";
"lng_action_theme_disabled" = "{from} disabled chat theme";
"lng_action_theme_disabled_channel" = "Channel theme disabled";
"lng_action_proximity_distance_m#one" = "{count} meter";
"lng_action_proximity_distance_m#other" = "{count} metres";
"lng_action_proximity_distance_km#one" = "{count} km";
@@ -1608,6 +1615,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_user_action_upload_photo" = "{user} is sending a photo";
"lng_send_action_upload_file" = "sending a file";
"lng_user_action_upload_file" = "{user} is sending a file";
"lng_send_action_choose_sticker" = "choosing a sticker";
"lng_user_action_choose_sticker" = "{user} is choosing a sticker";
"lng_unread_bar#one" = "{count} unread message";
"lng_unread_bar#other" = "{count} unread messages";
"lng_unread_bar_some" = "Unread messages";
@@ -1640,6 +1649,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_mark_read_sure" = "Are you sure you want to mark all chats from this folder as read?";
"lng_context_mark_read_all" = "Mark all chats as read";
"lng_context_mark_read_all_sure" = "Are you sure you want to mark all chats as read?";
"lng_context_mark_read_mentions_all" = "Mark all mentions as read";
"lng_context_archive_expand" = "Expand";
"lng_context_archive_collapse" = "Collapse";
"lng_context_archive_to_menu" = "Move to main menu";
@@ -2101,8 +2111,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_ptt_delay" = "Push to Talk release delay: {delay}";
"lng_group_call_share" = "Share Invite Link";
"lng_group_call_noise_suppression" = "Enable Noise Suppression";
"lng_group_call_limit#one" = "Video is only available\nfor the first {count} member";
"lng_group_call_limit#other" = "Video is only available\nfor the first {count} members";
"lng_group_call_over_limit#one" = "The voice chat is over {count} member.\nNew participants only have access to audio stream.";
"lng_group_call_over_limit#other" = "The voice chat is over {count} members.\nNew participants only have access to audio stream.";
"lng_group_call_video_paused" = "Video is paused";

View File

@@ -0,0 +1,17 @@
import os, sys, requests, re
os.chdir()
keys = []
with open('lang.strings') as f:
for line in f:
m = re.match(r'\"(lng_[a-z_]+)(\#[a-z]+)?\"', line)
if m:
keys.append(m.group(1))
elif not re.match(r'^\s*$', line):
print('Bad line: ' + line)
sys.exit(1)
print('Keys: ' + str(len(keys)))
sys.exit()

View File

@@ -92,7 +92,7 @@ inputPhotoFileLocation#40181ffe id:long access_hash:long file_reference:bytes th
inputPhotoLegacyFileLocation#d83466f3 id:long access_hash:long file_reference:bytes volume_id:long local_id:int secret:long = InputFileLocation;
inputPeerPhotoFileLocation#37257e99 flags:# big:flags.0?true peer:InputPeer photo_id:long = InputFileLocation;
inputStickerSetThumb#9d84f3db stickerset:InputStickerSet thumb_version:int = InputFileLocation;
inputGroupCallStream#bba51639 call:InputGroupCall time_ms:long scale:int = InputFileLocation;
inputGroupCallStream#598a92a flags:# call:InputGroupCall time_ms:long scale:int video_channel:flags.0?int video_quality:flags.0?int = InputFileLocation;
peerUser#9db1bc6d user_id:int = Peer;
peerChat#bad0e5bb chat_id:int = Peer;
@@ -128,8 +128,8 @@ chatForbidden#7328bdb id:int title:string = Chat;
channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#8a1e2983 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer = ChatFull;
channelFull#548c3f93 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer = ChatFull;
chatFull#49a0a5d9 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string = ChatFull;
channelFull#2f532f3c flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string = ChatFull;
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
@@ -187,6 +187,7 @@ messageActionGroupCall#7a0d7f42 flags:# call:InputGroupCall duration:flags.0?int
messageActionInviteToGroupCall#76b9f11a call:InputGroupCall users:Vector<int> = MessageAction;
messageActionSetMessagesTTL#aa1afbfd period:int = MessageAction;
messageActionGroupCallScheduled#b3a07661 call:InputGroupCall schedule_date:int = MessageAction;
messageActionSetChatTheme#aa786345 emoticon:string = MessageAction;
dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
@@ -234,7 +235,7 @@ inputReportReasonCopyright#9b89f93a = ReportReason;
inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
inputReportReasonFake#f5ddd6e7 = ReportReason;
userFull#139a9a77 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int = UserFull;
userFull#d697ff05 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string = UserFull;
contact#f911c994 user_id:int mutual:Bool = Contact;
@@ -465,6 +466,7 @@ sendMessageRecordRoundAction#88f27fbc = SendMessageAction;
sendMessageUploadRoundAction#243e1c66 progress:int = SendMessageAction;
speakingInGroupCallAction#d92c2285 = SendMessageAction;
sendMessageHistoryImportAction#dbda9246 progress:int = SendMessageAction;
sendMessageChooseStickerAction#b05ac6b1 = SendMessageAction;
contacts.found#b3134d9d my_results:Vector<Peer> results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found;
@@ -1116,7 +1118,7 @@ restrictionReason#d072acb4 platform:string reason:string text:string = Restricti
inputTheme#3c5693e9 id:long access_hash:long = InputTheme;
inputThemeSlug#f5890df1 slug:string = InputTheme;
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;
theme#e802b8dc flags:# creator:flags.0?true default:flags.1?true for_chat:flags.5?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?ThemeSettings installs_count:flags.4?int = Theme;
account.themesNotModified#f41eb622 = account.Themes;
account.themes#7f676421 hash:int themes:Vector<Theme> = account.Themes;
@@ -1135,9 +1137,9 @@ baseThemeNight#b7b31ea8 = BaseTheme;
baseThemeTinted#6d5f77ee = BaseTheme;
baseThemeArctic#5b11125a = BaseTheme;
inputThemeSettings#bd507cd1 flags:# base_theme:BaseTheme accent_color:int message_top_color:flags.0?int message_bottom_color:flags.0?int wallpaper:flags.1?InputWallPaper wallpaper_settings:flags.1?WallPaperSettings = InputThemeSettings;
inputThemeSettings#ff38f912 flags:# message_colors_animated:flags.2?true base_theme:BaseTheme accent_color:int message_colors:flags.0?Vector<int> wallpaper:flags.1?InputWallPaper wallpaper_settings:flags.1?WallPaperSettings = InputThemeSettings;
themeSettings#9c14984a flags:# base_theme:BaseTheme accent_color:int message_top_color:flags.0?int message_bottom_color:flags.0?int wallpaper:flags.1?WallPaper = ThemeSettings;
themeSettings#8db4e76c flags:# message_colors_animated:flags.2?true base_theme:BaseTheme accent_color:int message_colors:flags.0?Vector<int> wallpaper:flags.1?WallPaper = ThemeSettings;
webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector<Document> settings:flags.1?ThemeSettings = WebPageAttribute;
@@ -1195,7 +1197,7 @@ messageViews#455b853d flags:# views:flags.0?int forwards:flags.1?int replies:fla
messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> users:Vector<User> = messages.MessageViews;
messages.discussionMessage#f5dd8f9d flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
messages.discussionMessage#a6341782 flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
messageReplyHeader#a6d57763 flags:# reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
@@ -1206,7 +1208,7 @@ peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;
stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats;
groupCallDiscarded#7780bcb4 id:long access_hash:long duration:int = GroupCall;
groupCall#d597650c flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true can_start_video:flags.9?true id:long access_hash:long participants_count:int title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int unmuted_video_count:flags.10?int unmuted_video_limit:int version:int = GroupCall;
groupCall#d597650c flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true can_start_video:flags.9?true record_video_active:flags.11?true id:long access_hash:long participants_count:int title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int unmuted_video_count:flags.10?int unmuted_video_limit:int version:int = GroupCall;
inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall;
@@ -1265,6 +1267,15 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
chatTheme#ed0b5c33 emoticon:string theme:Theme dark_theme:Theme = ChatTheme;
account.chatThemesNotModified#e011e1c4 = account.ChatThemes;
account.chatThemes#fe4cbebd hash:int themes:Vector<ChatTheme> = account.ChatThemes;
sponsoredMessage#f671f0d1 flags:# random_id:bytes peer_id:Peer from_id:Peer message:string media:flags.0?MessageMedia entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
messages.sponsoredMessages#65a4c7d5 messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -1365,6 +1376,7 @@ account.setGlobalPrivacySettings#1edaaac2 settings:GlobalPrivacySettings = Globa
account.reportProfilePhoto#fa8cc6f5 peer:InputPeer photo_id:InputPhoto reason:ReportReason message:string = Bool;
account.resetPassword#9308ce1b = account.ResetPasswordResult;
account.declinePasswordReset#4c9409f6 = Bool;
account.getChatThemes#d6d71d7b hash:int = account.ChatThemes;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
@@ -1402,7 +1414,7 @@ messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates;
messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings;
messages.report#8953ab4e peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool;
@@ -1533,6 +1545,7 @@ messages.getAdminsWithInvites#3920e6ef peer:InputPeer = messages.ChatAdminsWithI
messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters;
messages.setHistoryTTL#b80e5fe4 peer:InputPeer period:int = Updates;
messages.checkHistoryImportPeer#5dc60f03 peer:InputPeer = messages.CheckedHistoryImportPeer;
messages.setChatTheme#e63be13f peer:InputPeer emoticon:string = Updates;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@@ -1611,6 +1624,8 @@ channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint addr
channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates;
channels.getInactiveChannels#11e831ee = messages.InactiveChats;
channels.convertToGigagroup#b290c69 channel:InputChannel = Updates;
channels.viewSponsoredMessage#beaedb94 channel:InputChannel random_id:bytes = Bool;
channels.getSponsoredMessages#ec210fbf channel:InputChannel = messages.SponsoredMessages;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
@@ -1649,10 +1664,10 @@ phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates;
phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector<InputUser> = Updates;
phone.discardGroupCall#7a777135 call:InputGroupCall = Updates;
phone.toggleGroupCallSettings#74bbb43d flags:# reset_invite_hash:flags.1?true call:InputGroupCall join_muted:flags.0?Bool = Updates;
phone.getGroupCall#c7cb017 call:InputGroupCall = phone.GroupCall;
phone.getGroupCall#41845db call:InputGroupCall limit:int = phone.GroupCall;
phone.getGroupParticipants#c558d8ab call:InputGroupCall ids:Vector<InputPeer> sources:Vector<int> offset:string limit:int = phone.GroupParticipants;
phone.checkGroupCall#b59cf977 call:InputGroupCall sources:Vector<int> = Vector<int>;
phone.toggleGroupCallRecord#c02a66d7 flags:# start:flags.0?true call:InputGroupCall title:flags.1?string = Updates;
phone.toggleGroupCallRecord#f128c708 flags:# start:flags.0?true video:flags.2?true call:InputGroupCall title:flags.1?string video_portrait:flags.2?Bool = Updates;
phone.editGroupCallParticipant#a5273abf flags:# call:InputGroupCall participant:InputPeer muted:flags.0?Bool volume:flags.1?int raise_hand:flags.2?Bool video_stopped:flags.3?Bool video_paused:flags.4?Bool presentation_paused:flags.5?Bool = Updates;
phone.editGroupCallTitle#1ca6ac0a call:InputGroupCall title:string = Updates;
phone.getGroupCallJoinAs#ef7c213a peer:InputPeer = phone.JoinAsPeers;
@@ -1678,4 +1693,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
// LAYER 131
// LAYER 132

View File

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

View File

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

View File

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

View File

@@ -496,11 +496,7 @@ int main(int argc, char *argv[])
#elif defined Q_OS_MAC
QString outName(QString("tmacupd%1").arg(AlphaVersion ? AlphaVersion : version));
#elif defined Q_OS_UNIX
#ifndef _LP64
QString outName(QString("tlinux32upd%1").arg(AlphaVersion ? AlphaVersion : version));
#else
QString outName(QString("tlinuxupd%1").arg(AlphaVersion ? AlphaVersion : version));
#endif
#else
#error Unknown platform!
#endif

View File

@@ -384,8 +384,6 @@ int main(int argc, char *argv[]) {
bool writeprotected = false;
bool tosettings = false;
bool startintray = false;
bool testmode = false;
bool externalupdater = false;
bool customWorkingDir = false;
char *key = 0;
@@ -399,10 +397,6 @@ int main(int argc, char *argv[]) {
debug = _debug = true;
} else if (equal(argv[i], "-startintray")) {
startintray = true;
} else if (equal(argv[i], "-testmode")) {
testmode = true;
} else if (equal(argv[i], "-externalupdater")) {
externalupdater = true;
} else if (equal(argv[i], "-tosettings")) {
tosettings = true;
} else if (equal(argv[i], "-workdir_custom")) {
@@ -503,8 +497,6 @@ int main(int argc, char *argv[]) {
if (autostart) push("-autostart");
if (debug) push("-debug");
if (startintray) push("-startintray");
if (testmode) push("-testmode");
if (externalupdater) push("-externalupdater");
if (tosettings) push("-tosettings");
if (key) {
push("-key");

View File

@@ -90,7 +90,7 @@ int main(int argc, const char * argv[]) {
openLog();
pid_t procId = 0;
BOOL update = YES, toSettings = NO, autoStart = NO, startInTray = NO, testMode = NO, freeType = NO, externalUpdater = NO;
BOOL update = YES, toSettings = NO, autoStart = NO, startInTray = NO, freeType = NO;
BOOL customWorkingDir = NO;
NSString *key = nil;
for (int i = 0; i < argc; ++i) {
@@ -114,12 +114,8 @@ int main(int argc, const char * argv[]) {
_debug = YES;
} else if ([@"-startintray" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
startInTray = YES;
} else if ([@"-testmode" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
testMode = YES;
} else if ([@"-freetype" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
freeType = YES;
} else if ([@"-externalupdater" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
externalUpdater = YES;
} else if ([@"-workdir_custom" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
customWorkingDir = YES;
} else if ([@"-key" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
@@ -256,9 +252,7 @@ int main(int argc, const char * argv[]) {
if (toSettings) [args addObject:@"-tosettings"];
if (_debug) [args addObject:@"-debug"];
if (startInTray) [args addObject:@"-startintray"];
if (testMode) [args addObject:@"-testmode"];
if (freeType) [args addObject:@"-freetype"];
if (externalUpdater) [args addObject:@"-externalupdater"];
if (autoStart) [args addObject:@"-autostart"];
if (key) {
[args addObject:@"-key"];

View File

@@ -343,7 +343,7 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdPara
LPWSTR *args;
int argsCount;
bool needupdate = false, autostart = false, debug = false, writeprotected = false, startintray = false, testmode = false, freetype = false, externalupdater = false;
bool needupdate = false, autostart = false, debug = false, writeprotected = false, startintray = false, freetype = false;
args = CommandLineToArgvW(GetCommandLine(), &argsCount);
if (args) {
for (int i = 1; i < argsCount; ++i) {
@@ -357,12 +357,8 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdPara
openLog();
} else if (equal(args[i], L"-startintray")) {
startintray = true;
} else if (equal(args[i], L"-testmode")) {
testmode = true;
} else if (equal(args[i], L"-freetype")) {
freetype = true;
} else if (equal(args[i], L"-externalupdater")) {
externalupdater = true;
} else if (equal(args[i], L"-writeprotected") && ++i < argsCount) {
writeLog(std::wstring(L"Argument: ") + args[i]);
writeprotected = true;
@@ -432,9 +428,7 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdPara
if (autostart) targs += L" -autostart";
if (debug) targs += L" -debug";
if (startintray) targs += L" -startintray";
if (testmode) targs += L" -testmode";
if (freetype) targs += L" -freetype";
if (externalupdater) targs += L" -externalupdater";
if (!customWorkingDir.empty()) {
targs += L" -workdir \"" + customWorkingDir + L"\"";
}

View File

@@ -112,6 +112,11 @@ ConfirmInviteBox::ConfirmInviteBox(
? tr::lng_channel_invite_private(tr::now)
: (!_participants.empty() && _participants.size() < count)
? tr::lng_group_invite_members(tr::now, lt_count, count)
: (count > 0 && _isChannel)
? tr::lng_chat_status_subscribers(
tr::now,
lt_count_decimal,
count)
: (count > 0)
? tr::lng_chat_status_members(tr::now, lt_count_decimal, count)
: _isChannel

View File

@@ -41,24 +41,6 @@ void CloudPassword::reload() {
}).send();
}
void CloudPassword::applyPendingReset(
const MTPaccount_ResetPasswordResult &data) {
if (!_state) {
reload();
return;
}
data.match([&](const MTPDaccount_resetPasswordOk &data) {
reload();
}, [&](const MTPDaccount_resetPasswordRequestedWait &data) {
const auto until = data.vuntil_date().v;
if (_state->pendingResetDate != until) {
_state->pendingResetDate = until;
_stateChanges.fire_copy(*_state);
}
}, [&](const MTPDaccount_resetPasswordFailedWait &data) {
});
}
void CloudPassword::clearUnconfirmedPassword() {
_requestId = _api.request(MTPaccount_CancelPasswordEmail(
)).done([=](const MTPBool &result) {
@@ -83,4 +65,48 @@ auto CloudPassword::stateCurrent() const
: std::nullopt;
}
auto CloudPassword::resetPassword()
-> rpl::producer<CloudPassword::ResetRetryDate, QString> {
return [=](auto consumer) {
_api.request(MTPaccount_ResetPassword(
)).done([=](const MTPaccount_ResetPasswordResult &result) {
result.match([&](const MTPDaccount_resetPasswordOk &data) {
reload();
}, [&](const MTPDaccount_resetPasswordRequestedWait &data) {
if (!_state) {
reload();
return;
}
const auto until = data.vuntil_date().v;
if (_state->pendingResetDate != until) {
_state->pendingResetDate = until;
_stateChanges.fire_copy(*_state);
}
}, [&](const MTPDaccount_resetPasswordFailedWait &data) {
consumer.put_next_copy(data.vretry_date().v);
});
consumer.put_done();
}).fail([=](const MTP::Error &error) {
consumer.put_error_copy(error.type());
}).send();
return rpl::lifetime();
};
}
auto CloudPassword::cancelResetPassword()
-> rpl::producer<rpl::no_value, QString> {
return [=](auto consumer) {
_api.request(MTPaccount_DeclinePasswordReset(
)).done([=] {
reload();
consumer.put_done();
}).fail([=](const MTP::Error &error) {
consumer.put_error_copy(error.type());
}).send();
return rpl::lifetime();
};
}
} // namespace Api

View File

@@ -23,14 +23,17 @@ namespace Api {
class CloudPassword final {
public:
using ResetRetryDate = int;
explicit CloudPassword(not_null<ApiWrap*> api);
void reload();
void applyPendingReset(const MTPaccount_ResetPasswordResult &data);
void clearUnconfirmedPassword();
rpl::producer<Core::CloudPasswordState> state() const;
std::optional<Core::CloudPasswordState> stateCurrent() const;
rpl::producer<ResetRetryDate, QString> resetPassword();
rpl::producer<rpl::no_value, QString> cancelResetPassword();
private:
MTP::Sender _api;
mtpRequestId _requestId = 0;

View File

@@ -129,6 +129,7 @@ void SendProgressManager::send(const Key &key, int progress) {
case Type::ChooseContact: return MTP_sendMessageChooseContactAction();
case Type::PlayGame: return MTP_sendMessageGamePlayAction();
case Type::Speaking: return MTP_speakingInGroupCallAction();
case Type::ChooseSticker: return MTP_sendMessageChooseStickerAction();
default: return MTP_sendMessageTypingAction();
}
}();

View File

@@ -30,6 +30,7 @@ enum class SendProgressType {
UploadFile,
ChooseLocation,
ChooseContact,
ChooseSticker,
PlayGame,
Speaking,
};

View File

@@ -32,7 +32,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/file_upload.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "app.h"
namespace Api {
namespace {

View File

@@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_histories.h"
#include "data/data_folder.h"
#include "data/data_scheduled_messages.h"
#include "data/data_send_action.h"
#include "lang/lang_cloud_manager.h"
#include "history/history.h"
#include "history/history_item.h"
@@ -1017,7 +1018,7 @@ void Updates::handleSendActionUpdate(
const auto when = requestingDifference()
? 0
: base::unixtime::now();
session().data().registerSendAction(
session().data().sendActionManager().registerFor(
history,
rootId,
from->asUser(),
@@ -2094,17 +2095,20 @@ void Updates::feedUpdate(const MTPUpdate &update) {
const auto msgId = d.vtop_msg_id().v;
const auto readTillId = d.vread_max_id().v;
const auto item = session().data().message(channelId, msgId);
const auto unreadCount = item
? session().data().countUnreadRepliesLocally(item, readTillId)
: std::nullopt;
if (item) {
item->setRepliesInboxReadTill(readTillId);
item->setRepliesInboxReadTill(readTillId, unreadCount);
if (const auto post = item->lookupDiscussionPostOriginal()) {
post->setRepliesInboxReadTill(readTillId);
post->setRepliesInboxReadTill(readTillId, unreadCount);
}
}
if (const auto broadcastId = d.vbroadcast_id()) {
if (const auto post = session().data().message(
broadcastId->v,
d.vbroadcast_post()->v)) {
post->setRepliesInboxReadTill(readTillId);
post->setRepliesInboxReadTill(readTillId, unreadCount);
}
}
} break;

View File

@@ -80,7 +80,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_media_prepare.h"
#include "storage/storage_account.h"
#include "facades.h"
#include "app.h"
#include "app.h" // App::quitting
namespace {
@@ -3505,7 +3505,7 @@ void ApiWrap::sharedMediaDone(
parsed.fullCount
));
if (type == SharedMediaType::Pinned && !parsed.messageIds.empty()) {
peer->setHasPinnedMessages(true);
peer->owner().history(peer)->setHasPinnedMessages(true);
}
}

View File

@@ -7,52 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "app.h"
#include "lang/lang_keys.h"
#include "boxes/confirm_box.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_abstract_structure.h"
#include "data/data_media_types.h"
#include "data/data_session.h"
#include "data/data_document.h"
#include "history/history.h"
#include "history/history_location_manager.h"
#include "history/history_item_components.h"
#include "history/view/history_view_element.h"
#include "media/audio/media_audio.h"
#include "ui/image/image.h"
#include "ui/cached_round_corners.h"
#include "inline_bots/inline_bot_layout_item.h"
#include "core/crash_reports.h"
#include "core/update_checker.h"
#include "core/sandbox.h"
#include "core/application.h"
#include "window/notifications_manager.h"
#include "window/window_controller.h"
#include "platform/platform_notifications_manager.h"
#include "storage/file_upload.h"
#include "storage/localstorage.h"
#include "storage/storage_facade.h"
#include "storage/storage_shared_media.h"
#include "mainwindow.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "main/main_session.h"
#include "styles/style_boxes.h"
#include "styles/style_overview.h"
#include "styles/style_media_view.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_chat.h"
#include "styles/style_layers.h"
#include <QtCore/QBuffer>
#include <QtGui/QFontDatabase>
#ifdef OS_MAC_OLD
#include <libexif/exif-data.h>
#endif // OS_MAC_OLD
namespace {
constexpr auto kImageAreaLimit = 12'032 * 9'024;
@@ -69,15 +32,6 @@ HistoryView::Element *hoveredItem = nullptr,
namespace App {
void initMedia() {
Ui::StartCachedCorners();
}
void deinitMedia() {
Ui::FinishCachedCorners();
Data::clearGlobalStructures();
}
void hoveredItem(HistoryView::Element *item) {
::hoveredItem = item;
}
@@ -168,80 +122,4 @@ namespace App {
App::quit();
}
QImage readImage(QByteArray data, QByteArray *format, bool opaque, bool *animated) {
if (data.isEmpty()) {
return QImage();
}
QByteArray tmpFormat;
QImage result;
QBuffer buffer(&data);
if (!format) {
format = &tmpFormat;
}
{
QImageReader reader(&buffer, *format);
#ifndef OS_MAC_OLD
reader.setAutoTransform(true);
#endif // OS_MAC_OLD
if (animated) *animated = reader.supportsAnimation() && reader.imageCount() > 1;
if (!reader.canRead()) {
return QImage();
}
const auto imageSize = reader.size();
if (imageSize.width() * imageSize.height() > kImageAreaLimit) {
return QImage();
}
QByteArray fmt = reader.format();
if (!fmt.isEmpty()) *format = fmt;
if (!reader.read(&result)) {
return QImage();
}
fmt = reader.format();
if (!fmt.isEmpty()) *format = fmt;
}
buffer.seek(0);
auto fmt = QString::fromUtf8(*format).toLower();
if (fmt == "jpg" || fmt == "jpeg") {
#ifdef OS_MAC_OLD
if (auto exifData = exif_data_new_from_data((const uchar*)(data.constData()), data.size())) {
auto byteOrder = exif_data_get_byte_order(exifData);
if (auto exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION)) {
auto orientationFix = [exifEntry, byteOrder] {
auto orientation = exif_get_short(exifEntry->data, byteOrder);
switch (orientation) {
case 2: return QTransform(-1, 0, 0, 1, 0, 0);
case 3: return QTransform(-1, 0, 0, -1, 0, 0);
case 4: return QTransform(1, 0, 0, -1, 0, 0);
case 5: return QTransform(0, -1, -1, 0, 0, 0);
case 6: return QTransform(0, 1, -1, 0, 0, 0);
case 7: return QTransform(0, 1, 1, 0, 0, 0);
case 8: return QTransform(0, -1, 1, 0, 0, 0);
}
return QTransform();
};
result = result.transformed(orientationFix());
}
exif_data_free(exifData);
}
#endif // OS_MAC_OLD
} else if (opaque) {
result = Images::prepareOpaque(std::move(result));
}
return result;
}
QImage readImage(const QString &file, QByteArray *format, bool opaque, bool *animated, QByteArray *content) {
QFile f(file);
if (f.size() > kImageSizeLimit || !f.open(QIODevice::ReadOnly)) {
if (animated) *animated = false;
return QImage();
}
auto imageBytes = f.readAll();
auto result = readImage(imageBytes, format, opaque, animated);
if (content && !result.isNull()) {
*content = imageBytes;
}
return result;
}
}

View File

@@ -7,8 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_types.h"
namespace HistoryView {
class Element;
} // namespace HistoryView
@@ -26,9 +24,6 @@ namespace App {
HistoryView::Element *mousedItem();
void clearMousedItems();
void initMedia();
void deinitMedia();
enum LaunchState {
Launched = 0,
QuitRequested = 1,
@@ -40,8 +35,4 @@ namespace App {
void setLaunchState(LaunchState state);
void restart();
constexpr auto kImageSizeLimit = 64 * 1024 * 1024; // Open images up to 64mb jpg/png/gif
QImage readImage(QByteArray data, QByteArray *format = nullptr, bool opaque = true, bool *animated = nullptr);
QImage readImage(const QString &file, QByteArray *format = nullptr, bool opaque = true, bool *animated = nullptr, QByteArray *content = 0);
};

View File

@@ -95,9 +95,7 @@ void AboutBox::showVersionHistory() {
url += qsl("win64/%1.zip");
} else if (Platform::IsMac()) {
url += qsl("mac/%1.zip");
} else if (Platform::IsLinux32Bit()) {
url += qsl("linux32/%1.tar.xz");
} else if (Platform::IsLinux64Bit()) {
} else if (Platform::IsLinux()) {
url += qsl("linux/%1.tar.xz");
} else {
Unexpected("Platform value.");

View File

@@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "mainwindow.h"
#include "app.h"
namespace Ui {
namespace internal {

View File

@@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/flat_set.h"
#include "base/openssl_help.h"
#include "boxes/confirm_box.h"
#include "boxes/confirm_phone_box.h" // ExtractPhonePrefix.
#include "boxes/peer_list_controllers.h"
#include "boxes/peers/add_participants_box.h"
#include "boxes/peers/edit_participant_box.h"
@@ -20,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/file_utilities.h"
#include "core/application.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "countries/countries_instance.h" // Countries::ExtractPhoneCode.
#include "window/window_session_controller.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
@@ -267,7 +267,7 @@ AddContactBox::AddContactBox(
this,
st::defaultInputField,
tr::lng_contact_phone(),
Ui::ExtractPhonePrefix(session->user()->phone()),
Countries::ExtractPhoneCode(session->user()->phone()),
phone)
, _invertOrder(langFirstNameGoesSecond()) {
if (!phone.isEmpty()) {

View File

@@ -7,41 +7,139 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/auto_lock_box.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "core/application.h"
#include "mainwindow.h"
#include "lang/lang_keys.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/time_input.h"
#include "ui/ui_utility.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
AutoLockBox::AutoLockBox(QWidget*, not_null<Main::Session*> session)
: _session(session) {
namespace {
constexpr auto kCustom = std::numeric_limits<int>::max();
constexpr auto kOptions = { 60, 300, 3600, 18000, kCustom };
constexpr auto kDefaultCustom = "10:00"_cs;
auto TimeString(int seconds) {
const auto hours = seconds / 3600;
const auto minutes = (seconds - hours * 3600) / 60;
return QString("%1:%2").arg(hours).arg(minutes, 2, 10, QLatin1Char('0'));
}
} // namespace
AutoLockBox::AutoLockBox(QWidget*) {
}
void AutoLockBox::prepare() {
setTitle(tr::lng_passcode_autolock());
addButton(tr::lng_box_ok(), [this] { closeBox(); });
addButton(tr::lng_box_ok(), [=] { closeBox(); });
auto options = { 60, 300, 3600, 18000 };
const auto currentTime = Core::App().settings().autoLock();
auto group = std::make_shared<Ui::RadiobuttonGroup>(
Core::App().settings().autoLock());
const auto group = std::make_shared<Ui::RadiobuttonGroup>(
ranges::contains(kOptions, currentTime) ? currentTime : kCustom);
const auto x = st::boxPadding.left() + st::boxOptionListPadding.left();
auto y = st::boxOptionListPadding.top() + st::autolockButton.margin.top();
auto count = int(options.size());
const auto count = int(kOptions.size());
_options.reserve(count);
for (auto seconds : options) {
_options.emplace_back(this, group, seconds, (seconds % 3600) ? tr::lng_passcode_autolock_minutes(tr::now, lt_count, seconds / 60) : tr::lng_passcode_autolock_hours(tr::now, lt_count, seconds / 3600), st::autolockButton);
_options.back()->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), y);
for (const auto seconds : kOptions) {
const auto text = [&] {
if (seconds == kCustom) {
return QString();
}
const auto minutes = (seconds % 3600);
return (minutes
? tr::lng_passcode_autolock_minutes
: tr::lng_passcode_autolock_hours)(
tr::now,
lt_count,
minutes ? (seconds / 60) : (seconds / 3600));
}();
_options.emplace_back(
this,
group,
seconds,
text,
st::autolockButton);
_options.back()->moveToLeft(x, y);
y += _options.back()->heightNoMargins() + st::boxOptionListSkip;
}
group->setChangedCallback([this](int value) { durationChanged(value); });
setDimensions(st::autolockWidth, st::boxOptionListPadding.top() + count * _options.back()->heightNoMargins() + (count - 1) * st::boxOptionListSkip + st::boxOptionListPadding.bottom() + st::boxPadding.bottom());
const auto timeInput = [&] {
const auto &last = _options.back();
const auto &st = st::autolockButton;
const auto textLeft = st.checkPosition.x()
+ last->checkRect().width()
+ st.textPosition.x();
const auto textTop = st.margin.top() + st.textPosition.y();
const auto timeInput = Ui::CreateChild<Ui::TimeInput>(
this,
(group->value() == kCustom)
? TimeString(currentTime)
: kDefaultCustom.utf8(),
st::autolockTimeField,
st::autolockDateField,
st::scheduleTimeSeparator,
st::scheduleTimeSeparatorPadding);
timeInput->resizeToWidth(st::autolockTimeWidth);
timeInput->moveToLeft(last->x() + textLeft, last->y() + textTop);
return timeInput;
}();
const auto collect = [=] {
const auto timeValue = timeInput->valueCurrent().split(':');
if (timeValue.size() != 2) {
return 0;
}
return timeValue[0].toInt() * 3600 + timeValue[1].toInt() * 60;
};
timeInput->focuses(
) | rpl::start_with_next([=] {
group->setValue(kCustom);
}, lifetime());
group->setChangedCallback([=](int value) {
if (value != kCustom) {
durationChanged(value);
} else {
timeInput->setFocusFast();
}
});
rpl::merge(
boxClosing() | rpl::filter([=] { return group->value() == kCustom; }),
timeInput->submitRequests()
) | rpl::start_with_next([=] {
if (const auto result = collect()) {
durationChanged(result);
} else {
timeInput->showError();
}
}, lifetime());
const auto timeInputBottom = timeInput->y() + timeInput->height();
setDimensions(
st::autolockWidth,
st::boxOptionListPadding.top()
+ (timeInputBottom - _options.back()->bottomNoMargins())
+ count * _options.back()->heightNoMargins()
+ (count - 1) * st::boxOptionListSkip
+ st::boxOptionListPadding.bottom()
+ st::boxPadding.bottom());
}
void AutoLockBox::durationChanged(int seconds) {
if (Core::App().settings().autoLock() == seconds) {
closeBox();
return;
}
Core::App().settings().setAutoLock(seconds);
Core::App().saveSettingsDelayed();

View File

@@ -9,17 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
namespace Main {
class Session;
} // namespace Main
namespace Ui {
class Radiobutton;
} // namespace Ui
class AutoLockBox : public Ui::BoxContent {
public:
AutoLockBox(QWidget*, not_null<Main::Session*> session);
AutoLockBox(QWidget*);
protected:
void prepare() override;
@@ -27,8 +23,6 @@ protected:
private:
void durationChanged(int seconds);
const not_null<Main::Session*> _session;
std::vector<object_ptr<Ui::Radiobutton>> _options;
};

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "ui/effects/round_checkbox.h"
#include "ui/image/image.h"
#include "ui/chat/chat_theme.h"
#include "ui/ui_utility.h"
#include "main/main_session.h"
#include "apiwrap.h"
@@ -255,7 +256,7 @@ void BackgroundBox::Inner::updatePapers() {
_papers = _session->data().wallpapers(
) | ranges::views::filter([](const Data::WallPaper &paper) {
return !paper.isPattern() || paper.backgroundColor().has_value();
return !paper.isPattern() || !paper.backgroundColors().empty();
}) | ranges::views::transform([](const Data::WallPaper &paper) {
return Paper{ paper };
}) | ranges::to_vector;
@@ -326,8 +327,18 @@ void BackgroundBox::Inner::validatePaperThumbnail(
paper.dataMedia = document->createMediaView();
paper.dataMedia->thumbnailWanted(paper.data.fileOrigin());
}
}
if (!paper.dataMedia || !paper.dataMedia->thumbnail()) {
if (!paper.dataMedia->thumbnail()) {
return;
}
} else if (!paper.data.backgroundColors().empty()) {
paper.thumbnail = Ui::PixmapFromImage(
Ui::GenerateBackgroundImage(
st::backgroundSize * cIntRetinaFactor(),
paper.data.backgroundColors(),
paper.data.gradientRotation()));
paper.thumbnail.setDevicePixelRatio(cRetinaFactor());
return;
} else {
return;
}
}
@@ -336,12 +347,11 @@ void BackgroundBox::Inner::validatePaperThumbnail(
: paper.dataMedia->thumbnail();
auto original = thumbnail->original();
if (paper.data.isPattern()) {
const auto color = *paper.data.backgroundColor();
original = Data::PreparePatternImage(
original = Ui::PreparePatternImage(
std::move(original),
color,
Data::PatternColor(color),
paper.data.patternIntensity());
paper.data.backgroundColors(),
paper.data.gradientRotation(),
paper.data.patternOpacity());
}
paper.thumbnail = Ui::PixmapFromImage(TakeMiddleSample(
original,

View File

@@ -10,6 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "window/themes/window_theme.h"
#include "ui/chat/chat_theme.h"
#include "ui/chat/chat_style.h"
#include "ui/toast/toast.h"
#include "ui/image/image.h"
#include "ui/widgets/checkbox.h"
@@ -279,7 +281,7 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
});
}
AdminLog::OwnedItem GenerateTextItem(
[[nodiscard]] AdminLog::OwnedItem GenerateTextItem(
not_null<HistoryView::ElementDelegate*> delegate,
not_null<History*> history,
const QString &text,
@@ -308,7 +310,7 @@ AdminLog::OwnedItem GenerateTextItem(
return AdminLog::OwnedItem(delegate, item);
}
QImage PrepareScaledNonPattern(
[[nodiscard]] QImage PrepareScaledNonPattern(
const QImage &image,
Images::Option blur) {
const auto size = st::boxWideWidth;
@@ -331,66 +333,31 @@ QImage PrepareScaledNonPattern(
size);
}
QImage ColorizePattern(QImage image, QColor color) {
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
image = std::move(image).convertToFormat(
QImage::Format_ARGB32_Premultiplied);
}
// Similar to style::colorizeImage.
// But style::colorizeImage takes pattern with all pixels having the
// same components value, from (0, 0, 0, 0) to (255, 255, 255, 255).
//
// While in patterns we have different value ranges, usually they are
// from (0, 0, 0, 0) to (0, 0, 0, 255), so we should use only 'alpha'.
const auto width = image.width();
const auto height = image.height();
const auto pattern = anim::shifted(color);
constexpr auto resultIntsPerPixel = 1;
const auto resultIntsPerLine = (image.bytesPerLine() >> 2);
const auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel;
auto resultInts = reinterpret_cast<uint32*>(image.bits());
Assert(resultIntsAdded >= 0);
Assert(image.depth() == static_cast<int>((resultIntsPerPixel * sizeof(uint32)) << 3));
Assert(image.bytesPerLine() == (resultIntsPerLine << 2));
const auto maskBytesPerPixel = (image.depth() >> 3);
const auto maskBytesPerLine = image.bytesPerLine();
const auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel;
// We want to read the last byte of four available.
// This is the difference with style::colorizeImage.
auto maskBytes = image.constBits() + (maskBytesPerPixel - 1);
Assert(maskBytesAdded >= 0);
Assert(image.depth() == (maskBytesPerPixel << 3));
for (auto y = 0; y != height; ++y) {
for (auto x = 0; x != width; ++x) {
auto maskOpacity = static_cast<anim::ShiftedMultiplier>(*maskBytes) + 1;
*resultInts = anim::unshifted(pattern * maskOpacity);
maskBytes += maskBytesPerPixel;
resultInts += resultIntsPerPixel;
}
maskBytes += maskBytesAdded;
resultInts += resultIntsAdded;
}
return image;
}
QImage PrepareScaledFromFull(
[[nodiscard]] QImage PrepareScaledFromFull(
const QImage &image,
std::optional<QColor> patternBackground,
bool isPattern,
const std::vector<QColor> &background,
int gradientRotation,
float64 patternOpacity,
Images::Option blur = Images::Option(0)) {
auto result = PrepareScaledNonPattern(image, blur);
if (patternBackground) {
result = ColorizePattern(
if (isPattern) {
result = Ui::PreparePatternImage(
std::move(result),
Data::PatternColor(*patternBackground));
background,
gradientRotation,
patternOpacity);
}
return std::move(result).convertToFormat(
QImage::Format_ARGB32_Premultiplied);
}
[[nodiscard]] QImage BlackImage(QSize size) {
auto result = QImage(size, QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::black);
return result;
}
} // namespace
BackgroundPreviewBox::BackgroundPreviewBox(
@@ -399,6 +366,7 @@ BackgroundPreviewBox::BackgroundPreviewBox(
const Data::WallPaper &paper)
: SimpleElementDelegate(controller, [=] { update(); })
, _controller(controller)
, _chatStyle(std::make_unique<Ui::ChatStyle>())
, _text1(GenerateTextItem(
delegate(),
_controller->session().data().history(PeerData::kServiceNotificationsId),
@@ -412,15 +380,33 @@ BackgroundPreviewBox::BackgroundPreviewBox(
, _paper(paper)
, _media(_paper.document() ? _paper.document()->createMediaView() : nullptr)
, _radial([=](crl::time now) { radialAnimationCallback(now); }) {
_chatStyle->apply(controller->defaultChatTheme().get());
if (_media) {
_media->thumbnailWanted(_paper.fileOrigin());
}
generateBackground();
_controller->session().downloaderTaskFinished(
) | rpl::start_with_next([=] {
update();
}, lifetime());
}
void BackgroundPreviewBox::generateBackground() {
if (_paper.backgroundColors().empty()) {
return;
}
const auto size = QSize(st::boxWideWidth, st::boxWideWidth)
* cIntRetinaFactor();
_generated = Ui::PixmapFromImage((_paper.patternOpacity() >= 0.)
? Ui::GenerateBackgroundImage(
size,
_paper.backgroundColors(),
_paper.gradientRotation())
: BlackImage(size));
_generated.setDevicePixelRatio(cRetinaFactor());
}
not_null<HistoryView::ElementDelegate*> BackgroundPreviewBox::delegate() {
return static_cast<HistoryView::ElementDelegate*>(this);
}
@@ -433,7 +419,7 @@ void BackgroundPreviewBox::prepare() {
if (_paper.hasShareUrl()) {
addLeftButton(tr::lng_background_share(), [=] { share(); });
}
updateServiceBg(_paper.backgroundColor());
updateServiceBg(_paper.backgroundColors());
_paper.loadDocument();
const auto document = _paper.document();
@@ -521,21 +507,23 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
Painter p(this);
const auto ms = crl::now();
const auto color = _paper.backgroundColor();
if (color) {
p.fillRect(e->rect(), *color);
if (_scaled.isNull()) {
setScaledFromThumb();
}
if (!color || _paper.isPattern()) {
if (!_scaled.isNull() || setScaledFromThumb()) {
paintImage(p);
paintRadial(p);
} else if (!color) {
p.fillRect(e->rect(), st::boxBg);
return;
} else {
// Progress of pattern loading.
paintRadial(p);
}
if (!_generated.isNull()
&& (_scaled.isNull()
|| (_fadeOutThumbnail.isNull() && _fadeIn.animating()))) {
p.drawPixmap(0, 0, _generated);
}
if (!_scaled.isNull()) {
paintImage(p);
paintRadial(p);
} else if (_generated.isNull()) {
p.fillRect(e->rect(), st::boxBg);
return;
} else {
// Progress of pattern loading.
paintRadial(p);
}
paintTexts(p, ms);
}
@@ -543,10 +531,6 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
void BackgroundPreviewBox::paintImage(Painter &p) {
Expects(!_scaled.isNull());
const auto master = _paper.isPattern()
? std::clamp(_paper.patternIntensity() / 100., 0., 1.)
: 1.;
const auto factor = cIntRetinaFactor();
const auto size = st::boxWideWidth;
const auto from = QRect(
@@ -563,7 +547,7 @@ void BackgroundPreviewBox::paintImage(Painter &p) {
const auto &pixmap = (!_blurred.isNull() && _paper.isBlurred())
? _blurred
: _scaled;
p.setOpacity(master * fade);
p.setOpacity(fade);
p.drawPixmap(rect(), pixmap, from);
checkBlurAnimationStart();
}
@@ -610,11 +594,15 @@ QRect BackgroundPreviewBox::radialRect() const {
void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
const auto height1 = _text1->height();
const auto height2 = _text2->height();
const auto context = _controller->defaultChatTheme()->preparePaintContext(
_chatStyle.get(),
rect(),
rect());
p.translate(0, textsTop());
paintDate(p);
_text1->draw(p, rect(), TextSelection(), ms);
_text1->draw(p, context);
p.translate(0, height1);
_text2->draw(p, rect(), TextSelection(), ms);
_text2->draw(p, context);
p.translate(0, height2);
}
@@ -655,7 +643,10 @@ void BackgroundPreviewBox::radialAnimationCallback(crl::time now) {
checkLoadedDocument();
}
bool BackgroundPreviewBox::setScaledFromThumb() {
void BackgroundPreviewBox::setScaledFromThumb() {
if (!_scaled.isNull()) {
return;
}
const auto localThumbnail = _paper.localThumbnail();
const auto thumbnail = localThumbnail
? localThumbnail
@@ -663,27 +654,29 @@ bool BackgroundPreviewBox::setScaledFromThumb() {
? _media->thumbnail()
: nullptr;
if (!thumbnail) {
return false;
return;
} else if (_paper.isPattern() && _paper.document() != nullptr) {
return false;
return;
}
auto scaled = PrepareScaledFromFull(
thumbnail->original(),
patternBackgroundColor(),
_paper.isPattern(),
_paper.backgroundColors(),
_paper.gradientRotation(),
_paper.patternOpacity(),
_paper.document() ? Images::Option::Blurred : Images::Option(0));
auto blurred = (_paper.document() || _paper.isPattern())
? QImage()
: PrepareScaledNonPattern(
Data::PrepareBlurredBackground(thumbnail->original()),
Ui::PrepareBlurredBackground(thumbnail->original()),
Images::Option(0));
setScaledFromImage(std::move(scaled), std::move(blurred));
return true;
}
void BackgroundPreviewBox::setScaledFromImage(
QImage &&image,
QImage &&blurred) {
updateServiceBg(Window::Theme::CountAverageColor(image));
updateServiceBg({ Ui::CountAverageColor(image) });
if (!_full.isNull()) {
startFadeInFrom(std::move(_scaled));
}
@@ -710,16 +703,20 @@ void BackgroundPreviewBox::checkBlurAnimationStart() {
startFadeInFrom(_paper.isBlurred() ? _scaled : _blurred);
}
void BackgroundPreviewBox::updateServiceBg(std::optional<QColor> background) {
if (background) {
_serviceBg = Window::Theme::AdjustedColor(
st::msgServiceBg->c,
*background);
void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
const auto count = int(bg.size());
if (!count) {
return;
}
}
std::optional<QColor> BackgroundPreviewBox::patternBackgroundColor() const {
return _paper.isPattern() ? _paper.backgroundColor() : std::nullopt;
auto red = 0, green = 0, blue = 0;
for (const auto &color : bg) {
red += color.red();
green += color.green();
blue += color.blue();
}
_serviceBg = Ui::ThemeAdjustedColor(
st::msgServiceBg->c,
QColor(red / count, green / count, blue / count));
}
void BackgroundPreviewBox::checkLoadedDocument() {
@@ -737,15 +734,23 @@ void BackgroundPreviewBox::checkLoadedDocument() {
crl::async([
this,
image = std::move(image),
patternBackground = patternBackgroundColor(),
isPattern = _paper.isPattern(),
background = _paper.backgroundColors(),
gradientRotation = _paper.gradientRotation(),
patternOpacity = _paper.patternOpacity(),
guard = _generating.make_guard()
]() mutable {
auto scaled = PrepareScaledFromFull(image, patternBackground);
auto blurred = patternBackground
? QImage()
: PrepareScaledNonPattern(
Data::PrepareBlurredBackground(image),
Images::Option(0));
auto scaled = PrepareScaledFromFull(
image,
isPattern,
background,
gradientRotation,
patternOpacity);
auto blurred = !isPattern
? PrepareScaledNonPattern(
Ui::PrepareBlurredBackground(image),
Images::Option(0))
: QImage();
crl::on_main(std::move(guard), [
this,
image = std::move(image),
@@ -758,9 +763,9 @@ void BackgroundPreviewBox::checkLoadedDocument() {
});
});
};
_generating = Data::ReadImageAsync(
_generating = Data::ReadBackgroundImageAsync(
_media.get(),
Window::Theme::PreprocessBackgroundImage,
Ui::PreprocessBackgroundImage,
generateCallback);
}
@@ -768,7 +773,7 @@ bool BackgroundPreviewBox::Start(
not_null<Window::SessionController*> controller,
const QString &slug,
const QMap<QString, QString> &params) {
if (const auto paper = Data::WallPaper::FromColorSlug(slug)) {
if (const auto paper = Data::WallPaper::FromColorsSlug(slug)) {
controller->show(Box<BackgroundPreviewBox>(
controller,
paper->withUrlParams(params)));

View File

@@ -25,6 +25,7 @@ class SessionController;
namespace Ui {
class Checkbox;
class ChatStyle;
} // namespace Ui
class BackgroundPreviewBox
@@ -56,11 +57,11 @@ private:
void radialAnimationCallback(crl::time now);
QRect radialRect() const;
void generateBackground();
void checkLoadedDocument();
bool setScaledFromThumb();
void setScaledFromThumb();
void setScaledFromImage(QImage &&image, QImage &&blurred);
void updateServiceBg(std::optional<QColor> background);
std::optional<QColor> patternBackgroundColor() const;
void updateServiceBg(const std::vector<QColor> &bg);
void paintImage(Painter &p);
void paintRadial(Painter &p);
void paintTexts(Painter &p, crl::time ms);
@@ -71,12 +72,13 @@ private:
void checkBlurAnimationStart();
const not_null<Window::SessionController*> _controller;
std::unique_ptr<Ui::ChatStyle> _chatStyle;
AdminLog::OwnedItem _text1;
AdminLog::OwnedItem _text2;
Data::WallPaper _paper;
std::shared_ptr<Data::DocumentMedia> _media;
QImage _full;
QPixmap _scaled, _blurred, _fadeOutThumbnail;
QPixmap _generated, _scaled, _blurred, _fadeOutThumbnail;
Ui::Animations::Simple _fadeIn;
Ui::RadialAnimation _radial;
base::binary_guard _generating;

View File

@@ -324,7 +324,7 @@ passcodePadding: margins(0px, 0px, 0px, 5px);
passcodeTextLine: 28px;
passcodeLittleSkip: 5px;
passcodeAboutSkip: 7px;
passcodeSkip: 20px;
passcodeSkip: 23px;
newGroupAboutFg: windowSubTextFg;
newGroupPadding: margins(4px, 6px, 4px, 3px);
@@ -965,3 +965,11 @@ scheduleTimeSeparatorPadding: margins(2px, 0px, 2px, 0px);
boxAttentionDividerLabel: FlatLabel(boxDividerLabel) {
textFg: boxTextFgError;
}
autolockDateField: InputField(scheduleDateField) {
heightMin: 22px;
}
autolockTimeField: InputField(scheduleTimeField) {
heightMin: 20px;
}
autolockTimeWidth: 52px;

View File

@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/special_fields.h"
#include "boxes/confirm_phone_box.h"
#include "boxes/confirm_box.h"
#include "countries/countries_instance.h" // Countries::ExtractPhoneCode.
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_user.h"
@@ -151,7 +152,7 @@ void ChangePhoneBox::EnterPhone::prepare() {
this,
st::defaultInputField,
tr::lng_change_phone_new_title(),
Ui::ExtractPhonePrefix(_session->user()->phone()),
Countries::ExtractPhoneCode(_session->user()->phone()),
phoneValue);
_phone->resize(st::boxWidth - 2 * st::boxPadding.left(), _phone->height());

View File

@@ -41,7 +41,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "mtproto/mtproto_config.h"
#include "facades.h" // Ui::showChatsList
#include "app.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -282,7 +281,8 @@ void ConfirmBox::confirmed() {
}
} else if (const auto callbackPtr = std::get_if<2>(confirmed)) {
if (auto callback = base::take(*callbackPtr)) {
callback([=] { closeBox(); });
const auto weak = Ui::MakeWeak(this);
callback(crl::guard(weak, [=] { closeBox(); }));
}
}
}

View File

@@ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_info.h"
#include "main/main_session.h"
#include "mainwidget.h"
#include "numbers.h"
#include "lang/lang_keys.h"
#include "mtproto/facade.h"
#include "styles/style_layers.h"

View File

@@ -67,10 +67,7 @@ auto ListFromMimeData(not_null<const QMimeData*> data) {
if (result.error == Error::None) {
return result;
} else if (data->hasImage()) {
auto image = Platform::GetImageFromClipboard();
if (image.isNull()) {
image = qvariant_cast<QImage>(data->imageData());
}
auto image = qvariant_cast<QImage>(data->imageData());
if (!image.isNull()) {
return Storage::PrepareMediaFromImage(
std::move(image),

View File

@@ -14,7 +14,6 @@ Copyright (C) 2017, Nicholas Guriev <guriev-ns@ya.ru>
#include "ui/special_buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "app.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"

View File

@@ -102,49 +102,53 @@ void StartPendingReset(
not_null<Ui::BoxContent*> context,
Fn<void()> close) {
const auto weak = Ui::MakeWeak(context.get());
session->api().request(MTPaccount_ResetPassword(
)).done([=](const MTPaccount_ResetPasswordResult &result) {
session->api().cloudPassword().applyPendingReset(result);
result.match([&](const MTPDaccount_resetPasswordOk &data) {
}, [&](const MTPDaccount_resetPasswordRequestedWait &data) {
}, [&](const MTPDaccount_resetPasswordFailedWait &data) {
constexpr auto kMinute = 60;
constexpr auto kHour = 3600;
constexpr auto kDay = 86400;
const auto left = std::max(
data.vretry_date().v - base::unixtime::now(),
kMinute);
const auto days = (left / kDay);
const auto hours = (left / kHour);
const auto minutes = (left / kMinute);
const auto duration = days
? tr::lng_group_call_duration_days(tr::now, lt_count, days)
: hours
? tr::lng_group_call_duration_hours(tr::now, lt_count, hours)
: tr::lng_group_call_duration_minutes(
tr::now,
lt_count,
minutes);
if (const auto strong = weak.data()) {
strong->getDelegate()->show(Box<InformBox>(
tr::lng_cloud_password_reset_later(
tr::now,
lt_duration,
duration)));
auto lifetime = std::make_shared<rpl::lifetime>();
auto finish = [=](const QString &message) mutable {
if (const auto strong = weak.data()) {
if (!message.isEmpty()) {
strong->getDelegate()->show(Box<InformBox>(message));
}
});
if (const auto strong = weak.data()) {
strong->closeBox();
}
close();
}).fail([=](const MTP::Error &error) {
if (const auto strong = weak.data()) {
strong->getDelegate()->show(
Box<InformBox>("Error: " + error.type()));
strong->closeBox();
if (lifetime) {
base::take(lifetime)->destroy();
}
close();
}).send();
};
session->api().cloudPassword().resetPassword(
) | rpl::start_with_next_error_done([=](
Api::CloudPassword::ResetRetryDate retryDate) {
constexpr auto kMinute = 60;
constexpr auto kHour = 3600;
constexpr auto kDay = 86400;
const auto left = std::max(
retryDate - base::unixtime::now(),
kMinute);
const auto days = (left / kDay);
const auto hours = (left / kHour);
const auto minutes = (left / kMinute);
const auto duration = days
? tr::lng_group_call_duration_days(tr::now, lt_count, days)
: hours
? tr::lng_group_call_duration_hours(tr::now, lt_count, hours)
: tr::lng_group_call_duration_minutes(
tr::now,
lt_count,
minutes);
if (const auto strong = weak.data()) {
strong->getDelegate()->show(Box<InformBox>(
tr::lng_cloud_password_reset_later(
tr::now,
lt_duration,
duration)));
}
}, [=](const QString &error) mutable {
finish("Error: " + error);
}, [=]() mutable {
finish({});
}, *lifetime);
}
} // namespace
@@ -185,21 +189,33 @@ PasscodeBox::PasscodeBox(
PasscodeBox::PasscodeBox(
QWidget*,
not_null<Main::Session*> session,
not_null<MTP::Instance*> mtp,
Main::Session *session,
const CloudFields &fields)
: _session(session)
, _api(&_session->mtp())
, _api(mtp)
, _turningOff(fields.turningOff)
, _cloudPwd(true)
, _cloudFields(fields)
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
, _oldPasscode(this, st::defaultInputField, tr::lng_cloud_password_enter_old())
, _newPasscode(this, st::defaultInputField, fields.curRequest ? tr::lng_cloud_password_enter_new() : tr::lng_cloud_password_enter_first())
, _newPasscode(
this,
st::defaultInputField,
(fields.curRequest
? tr::lng_cloud_password_enter_new()
: tr::lng_cloud_password_enter_first()))
, _reenterPasscode(this, st::defaultInputField, tr::lng_cloud_password_confirm_new())
, _passwordHint(this, st::defaultInputField, fields.curRequest ? tr::lng_cloud_password_change_hint() : tr::lng_cloud_password_hint())
, _passwordHint(
this,
st::defaultInputField,
(fields.curRequest
? tr::lng_cloud_password_change_hint()
: tr::lng_cloud_password_hint()))
, _recoverEmail(this, st::defaultInputField, tr::lng_cloud_password_email())
, _recover(this, tr::lng_signin_recover(tr::now))
, _showRecoverLink(_cloudFields.hasRecovery || !_cloudFields.pendingResetDate) {
Expects(session != nullptr || !fields.fromRecoveryCode.isEmpty());
Expects(!_turningOff || _cloudFields.curRequest);
if (!_cloudFields.hint.isEmpty()) {
@@ -209,6 +225,13 @@ PasscodeBox::PasscodeBox(
}
}
PasscodeBox::PasscodeBox(
QWidget*,
not_null<Main::Session*> session,
const CloudFields &fields)
: PasscodeBox(nullptr, &session->mtp(), session, fields) {
}
rpl::producer<QByteArray> PasscodeBox::newPasswordSet() const {
return _newPasswordSet.events();
}
@@ -221,6 +244,10 @@ rpl::producer<> PasscodeBox::clearUnconfirmedPassword() const {
return _clearUnconfirmedPassword.events();
}
rpl::producer<MTPauth_Authorization> PasscodeBox::newAuthorization() const {
return _newAuthorization.events();
}
bool PasscodeBox::currentlyHave() const {
return _cloudPwd
? (!!_cloudFields.curRequest)
@@ -268,9 +295,11 @@ void PasscodeBox::prepare() {
} else {
_oldPasscode->hide();
setTitle(_cloudPwd
? tr::lng_cloud_password_create()
? (_cloudFields.fromRecoveryCode.isEmpty()
? tr::lng_cloud_password_create()
: tr::lng_cloud_password_change())
: tr::lng_passcode_create());
setDimensions(st::boxWidth, st::passcodePadding.top() + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + (_cloudPwd ? (st::passcodeLittleSkip + _recoverEmail->height() + st::passcodeSkip) : st::passcodePadding.bottom()));
setDimensions(st::boxWidth, st::passcodePadding.top() + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + ((_cloudPwd && _cloudFields.fromRecoveryCode.isEmpty()) ? (st::passcodeLittleSkip + _recoverEmail->height() + st::passcodeSkip) : st::passcodePadding.bottom()));
}
}
@@ -297,7 +326,10 @@ void PasscodeBox::prepare() {
_newPasscode->setVisible(!onlyCheck);
_reenterPasscode->setVisible(!onlyCheck);
_passwordHint->setVisible(!onlyCheck && _cloudPwd);
_recoverEmail->setVisible(!onlyCheck && _cloudPwd && !has);
_recoverEmail->setVisible(!onlyCheck
&& _cloudPwd
&& !has
&& _cloudFields.fromRecoveryCode.isEmpty());
}
void PasscodeBox::submit() {
@@ -396,21 +428,44 @@ void PasscodeBox::setInnerFocus() {
}
}
void PasscodeBox::recoverPasswordDone(
const QByteArray &newPasswordBytes,
const MTPauth_Authorization &result) {
if (_replacedBy) {
_replacedBy->closeBox();
}
_setRequest = 0;
const auto weak = Ui::MakeWeak(this);
_newAuthorization.fire_copy(result);
if (weak) {
_newPasswordSet.fire_copy(newPasswordBytes);
if (weak) {
getDelegate()->show(Box<InformBox>(
tr::lng_cloud_password_updated(tr::now)));
if (weak) {
closeBox();
}
}
}
}
void PasscodeBox::setPasswordDone(const QByteArray &newPasswordBytes) {
if (_replacedBy) {
_replacedBy->closeBox();
}
_setRequest = 0;
_newPasswordSet.fire_copy(newPasswordBytes);
const auto weak = Ui::MakeWeak(this);
const auto text = _reenterPasscode->isHidden()
? tr::lng_cloud_password_removed(tr::now)
: _oldPasscode->isHidden()
? tr::lng_cloud_password_was_set(tr::now)
: tr::lng_cloud_password_updated(tr::now);
getDelegate()->show(Box<InformBox>(text));
_newPasswordSet.fire_copy(newPasswordBytes);
if (weak) {
closeBox();
const auto text = _reenterPasscode->isHidden()
? tr::lng_cloud_password_removed(tr::now)
: _oldPasscode->isHidden()
? tr::lng_cloud_password_was_set(tr::now)
: tr::lng_cloud_password_updated(tr::now);
getDelegate()->show(Box<InformBox>(text));
if (weak) {
closeBox();
}
}
}
@@ -795,25 +850,44 @@ void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
}
const auto hint = _passwordHint->getLastText();
const auto email = _recoverEmail->getLastText().trimmed();
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_hint
| MTPDaccount_passwordInputSettings::Flag::f_email;
using Flag = MTPDaccount_passwordInputSettings::Flag;
const auto flags = Flag::f_new_algo
| Flag::f_new_password_hash
| Flag::f_hint
| (_cloudFields.fromRecoveryCode.isEmpty() ? Flag::f_email : Flag(0));
_checkPasswordCallback = nullptr;
_setRequest = _api.request(MTPaccount_UpdatePasswordSettings(
MTP_inputCheckPasswordEmpty(),
MTP_account_passwordInputSettings(
MTP_flags(flags),
Core::PrepareCloudPasswordAlgo(_cloudFields.newAlgo),
MTP_bytes(newPasswordHash.modpow),
MTP_string(hint),
MTP_string(email),
MTPSecureSecretSettings())
)).done([=](const MTPBool &result) {
setPasswordDone(newPasswordBytes);
}).fail([=](const MTP::Error &error) {
setPasswordFail(newPasswordBytes, email, error);
}).handleFloodErrors().send();
const auto settings = MTP_account_passwordInputSettings(
MTP_flags(flags),
Core::PrepareCloudPasswordAlgo(_cloudFields.newAlgo),
MTP_bytes(newPasswordHash.modpow),
MTP_string(hint),
MTP_string(email),
MTPSecureSecretSettings());
if (_cloudFields.fromRecoveryCode.isEmpty()) {
_setRequest = _api.request(MTPaccount_UpdatePasswordSettings(
MTP_inputCheckPasswordEmpty(),
settings
)).done([=](const MTPBool &result) {
setPasswordDone(newPasswordBytes);
}).fail([=](const MTP::Error &error) {
setPasswordFail(newPasswordBytes, email, error);
}).handleFloodErrors().send();
} else {
_setRequest = _api.request(MTPauth_RecoverPassword(
MTP_flags(MTPauth_RecoverPassword::Flag::f_new_settings),
MTP_string(_cloudFields.fromRecoveryCode),
settings
)).done([=](const MTPauth_Authorization &result) {
recoverPasswordDone(newPasswordBytes, result);
}).fail([=](const MTP::Error &error) {
if (MTP::IsFloodError(error)) {
_newError = tr::lng_flood_error(tr::now);
update();
}
setPasswordFail(newPasswordBytes, email, error);
}).handleFloodErrors().send();
}
}
void PasscodeBox::changeCloudPassword(
@@ -1001,6 +1075,7 @@ void PasscodeBox::emailChanged() {
void PasscodeBox::recoverByEmail() {
if (!_cloudFields.hasRecovery) {
Assert(_session != nullptr);
const auto session = _session;
const auto confirmBox = std::make_shared<QPointer<BoxContent>>();
const auto reset = crl::guard(this, [=] {
@@ -1032,19 +1107,19 @@ void PasscodeBox::recoverExpired() {
}
void PasscodeBox::recover() {
if (_pattern == "-") return;
if (_pattern == "-" || !_session) {
return;
}
const auto weak = Ui::MakeWeak(this);
const auto box = getDelegate()->show(Box<RecoverBox>(
&_api.instance(),
_session,
_pattern,
_cloudFields.notEmptyPassport,
_cloudFields.pendingResetDate != 0,
_cloudFields,
[weak] { if (weak) { weak->closeBox(); } }));
box->passwordCleared(
) | rpl::map_to(
QByteArray()
box->newPasswordSet(
) | rpl::start_to_stream(_newPasswordSet, lifetime());
box->recoveryExpired(
@@ -1067,18 +1142,19 @@ void PasscodeBox::recoverStartFail(const MTP::Error &error) {
RecoverBox::RecoverBox(
QWidget*,
not_null<Main::Session*> session,
not_null<MTP::Instance*> mtp,
Main::Session *session,
const QString &pattern,
bool notEmptyPassport,
bool hasPendingReset,
const PasscodeBox::CloudFields &fields,
Fn<void()> closeParent)
: _api(&session->mtp())
: _session(session)
, _api(mtp)
, _pattern(st::normalFont->elided(tr::lng_signin_recover_hint(tr::now, lt_recover_email, pattern), st::boxWidth - st::boxPadding.left() * 1.5))
, _notEmptyPassport(notEmptyPassport)
, _cloudFields(fields)
, _recoverCode(this, st::defaultInputField, tr::lng_signin_code())
, _noEmailAccess(this, tr::lng_signin_try_password(tr::now))
, _closeParent(std::move(closeParent)) {
if (hasPendingReset) {
if (_cloudFields.pendingResetDate != 0 || !session) {
_noEmailAccess.destroy();
} else {
_noEmailAccess->setClickedCallback([=] {
@@ -1102,8 +1178,8 @@ RecoverBox::RecoverBox(
}
}
rpl::producer<> RecoverBox::passwordCleared() const {
return _passwordCleared.events();
rpl::producer<QByteArray> RecoverBox::newPasswordSet() const {
return _newPasswordSet.events();
}
rpl::producer<> RecoverBox::recoveryExpired() const {
@@ -1169,17 +1245,29 @@ void RecoverBox::submit() {
}
const auto send = crl::guard(this, [=] {
_submitRequest = _api.request(MTPauth_RecoverPassword(
MTP_flags(0),
MTP_string(code),
MTPaccount_PasswordInputSettings()
)).done([=](const MTPauth_Authorization &result) {
codeSubmitDone(result);
}).fail([=](const MTP::Error &error) {
codeSubmitFail(error);
}).handleFloodErrors().send();
if (_cloudFields.turningOff) {
// From "Disable cloud password".
_submitRequest = _api.request(MTPauth_RecoverPassword(
MTP_flags(0),
MTP_string(code),
MTPaccount_PasswordInputSettings()
)).done([=](const MTPauth_Authorization &result) {
proceedToClear();
}).fail([=](const MTP::Error &error) {
checkSubmitFail(error);
}).handleFloodErrors().send();
} else {
// From "Change cloud password".
_submitRequest = _api.request(MTPauth_CheckRecoveryPassword(
MTP_string(code)
)).done([=](const MTPBool &result) {
proceedToChange(code);
}).fail([=](const MTP::Error &error) {
checkSubmitFail(error);
}).handleFloodErrors().send();
}
});
if (_notEmptyPassport) {
if (_cloudFields.notEmptyPassport) {
const auto confirmed = [=](Fn<void()> &&close) {
send();
close();
@@ -1205,16 +1293,46 @@ void RecoverBox::codeChanged() {
setError(QString());
}
void RecoverBox::codeSubmitDone(const MTPauth_Authorization &result) {
void RecoverBox::proceedToClear() {
_submitRequest = 0;
_passwordCleared.fire({});
_newPasswordSet.fire({});
getDelegate()->show(
Box<InformBox>(tr::lng_cloud_password_removed(tr::now)),
Ui::LayerOption::CloseOther);
}
void RecoverBox::codeSubmitFail(const MTP::Error &error) {
void RecoverBox::proceedToChange(const QString &code) {
Expects(!_cloudFields.turningOff);
_submitRequest = 0;
auto fields = _cloudFields;
fields.fromRecoveryCode = code;
fields.hasRecovery = false;
// we could've been turning off, no need to force new password then
// like if (_cloudFields.turningOff) { just RecoverPassword else Check }
fields.curRequest = {};
auto box = Box<PasscodeBox>(_session, fields);
box->boxClosing(
) | rpl::start_with_next([=] {
const auto weak = Ui::MakeWeak(this);
if (const auto onstack = _closeParent) {
onstack();
}
if (weak) {
weak->closeBox();
}
}, lifetime());
box->newPasswordSet(
) | rpl::start_with_next([=](QByteArray &&password) {
_newPasswordSet.fire(std::move(password));
}, lifetime());
getDelegate()->show(std::move(box));
}
void RecoverBox::checkSubmitFail(const MTP::Error &error) {
if (MTP::IsFloodError(error)) {
_submitRequest = 0;
setError(tr::lng_flood_error(tr::now));
@@ -1225,7 +1343,7 @@ void RecoverBox::codeSubmitFail(const MTP::Error &error) {
const QString &err = error.type();
if (err == qstr("PASSWORD_EMPTY")) {
_passwordCleared.fire({});
_newPasswordSet.fire(QByteArray());
getDelegate()->show(
Box<InformBox>(tr::lng_cloud_password_removed(tr::now)),
Ui::LayerOption::CloseOther);

View File

@@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h"
#include "core/core_cloud_password.h"
namespace MTP {
class Instance;
} // namespace MTP
namespace Main {
class Session;
} // namespace Main
@@ -35,6 +39,7 @@ public:
Core::CloudPasswordCheckRequest curRequest;
Core::CloudPasswordAlgo newAlgo;
bool hasRecovery = false;
QString fromRecoveryCode;
bool notEmptyPassport = false;
QString hint;
Core::SecureSecretAlgo newSecureSecretAlgo;
@@ -47,6 +52,11 @@ public:
std::optional<QString> customDescription;
rpl::producer<QString> customSubmitButton;
};
PasscodeBox(
QWidget*,
not_null<MTP::Instance*> mtp,
Main::Session *session,
const CloudFields &fields);
PasscodeBox(
QWidget*,
not_null<Main::Session*> session,
@@ -56,6 +66,8 @@ public:
rpl::producer<> passwordReloadNeeded() const;
rpl::producer<> clearUnconfirmedPassword() const;
rpl::producer<MTPauth_Authorization> newAuthorization() const;
bool handleCustomCheckError(const MTP::Error &error);
bool handleCustomCheckError(const QString &type);
@@ -83,6 +95,9 @@ private:
bool onlyCheckCurrent() const;
void setPasswordDone(const QByteArray &newPasswordBytes);
void recoverPasswordDone(
const QByteArray &newPasswordBytes,
const MTPauth_Authorization &result);
void setPasswordFail(const MTP::Error &error);
void setPasswordFail(const QString &type);
void setPasswordFail(
@@ -132,7 +147,7 @@ private:
void passwordChecked();
void serverError();
const not_null<Main::Session*> _session;
Main::Session *_session = nullptr;
MTP::Sender _api;
QString _pattern;
@@ -163,6 +178,7 @@ private:
QString _oldError, _newError, _emailError;
rpl::event_stream<QByteArray> _newPasswordSet;
rpl::event_stream<MTPauth_Authorization> _newAuthorization;
rpl::event_stream<> _passwordReloadNeeded;
rpl::event_stream<> _clearUnconfirmedPassword;
@@ -172,14 +188,14 @@ class RecoverBox final : public Ui::BoxContent {
public:
RecoverBox(
QWidget*,
not_null<Main::Session*> session,
not_null<MTP::Instance*> mtp,
Main::Session *session,
const QString &pattern,
bool notEmptyPassport,
bool hasPendingReset,
const PasscodeBox::CloudFields &fields,
Fn<void()> closeParent = nullptr);
rpl::producer<> passwordCleared() const;
rpl::producer<> recoveryExpired() const;
[[nodiscard]] rpl::producer<QByteArray> newPasswordSet() const;
[[nodiscard]] rpl::producer<> recoveryExpired() const;
//void reloadPassword();
//void recoveryExpired();
@@ -194,15 +210,18 @@ protected:
private:
void submit();
void codeChanged();
void codeSubmitDone(const MTPauth_Authorization &result);
void codeSubmitFail(const MTP::Error &error);
void proceedToClear();
void proceedToChange(const QString &code);
void checkSubmitFail(const MTP::Error &error);
void setError(const QString &error);
Main::Session *_session = nullptr;
MTP::Sender _api;
mtpRequestId _submitRequest = 0;
QString _pattern;
bool _notEmptyPassport = false;
PasscodeBox::CloudFields _cloudFields;
object_ptr<Ui::InputField> _recoverCode;
object_ptr<Ui::LinkButton> _noEmailAccess;
@@ -210,7 +229,7 @@ private:
QString _error;
rpl::event_stream<> _passwordCleared;
rpl::event_stream<QByteArray> _newPasswordSet;
rpl::event_stream<> _recoveryExpired;
};

View File

@@ -30,7 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/profile/info_profile_icon.h"
#include "apiwrap.h"
#include "facades.h" // Ui::showPeerHistory
#include "app.h"
#include "styles/style_boxes.h"
namespace {

View File

@@ -47,7 +47,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/vertical_layout.h"
#include "window/window_session_controller.h"
#include "info/profile/info_profile_icon.h"
#include "app.h"
#include "apiwrap.h"
#include "api/api_invite_links.h"
#include "facades.h"

View File

@@ -27,7 +27,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "mainwindow.h"
#include "apiwrap.h"
#include "app.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"

View File

@@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/send_files_box.h"
#include "platform/platform_specific.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "storage/storage_media_prepare.h"
@@ -52,7 +51,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "core/core_settings.h"
#include "facades.h" // App::LambdaDelayed.
#include "app.h"
#include "styles/style_chat.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -800,10 +798,7 @@ bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
if (result.error == Ui::PreparedList::Error::None) {
return result;
} else if (data->hasImage()) {
auto image = Platform::GetImageFromClipboard();
if (image.isNull()) {
image = qvariant_cast<QImage>(data->imageData());
}
auto image = qvariant_cast<QImage>(data->imageData());
if (!image.isNull()) {
return Storage::PrepareMediaFromImage(
std::move(image),

View File

@@ -19,7 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include "app.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"

View File

@@ -26,7 +26,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/confirm_box.h"
#include "base/unixtime.h"
#include "api/api_updates.h"
#include "app.h"
#include "apiwrap.h"
#include "styles/style_layers.h" // st::boxLabel.
#include "styles/style_calls.h"

View File

@@ -736,7 +736,7 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
.receiveTimeout = serverConfig.callPacketTimeoutMs / 1000.,
.dataSaving = tgcalls::DataSaving::Never,
.enableP2P = call.is_p2p_allowed(),
.enableAEC = !Platform::IsMac10_7OrGreater(),
.enableAEC = false,
.enableNS = true,
.enableAGC = true,
.enableVolumeControl = true,

View File

@@ -34,7 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "mtproto/mtproto_config.h"
#include "boxes/rate_call_box.h"
#include "app.h"
#include "app.h" // App::quitting
#include <tgcalls/VideoCaptureInterface.h>
#include <tgcalls/StaticThreads.h>

View File

@@ -45,7 +45,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/platform_specific.h"
#include "base/platform/base_platform_info.h"
#include "window/main_window.h"
#include "app.h"
#include "webrtc/webrtc_video_track.h"
#include "webrtc/webrtc_media_devices.h"
#include "styles/style_calls.h"

View File

@@ -32,7 +32,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "boxes/abstract_box.h"
#include "base/timer.h"
#include "app.h"
#include "styles/style_calls.h"
#include "styles/style_chat.h" // style::GroupCallUserpics
#include "styles/style_layers.h"

View File

@@ -126,11 +126,19 @@ using JoinClientFields = std::variant<
class GroupCall::LoadPartTask final : public tgcalls::BroadcastPartTask {
public:
using Quality = tgcalls::VideoChannelDescription::Quality;
LoadPartTask(
base::weak_ptr<GroupCall> call,
int64 time,
int64 period,
Fn<void(tgcalls::BroadcastPart&&)> done);
LoadPartTask(
base::weak_ptr<GroupCall> call,
int64 time,
int64 period,
int32 videoChannel,
Quality videoQuality,
Fn<void(tgcalls::BroadcastPart&&)> done);
[[nodiscard]] int64 time() const {
return _time;
@@ -138,6 +146,12 @@ public:
[[nodiscard]] int32 scale() const {
return _scale;
}
[[nodiscard]] int32 videoChannel() const {
return _videoChannel;
}
[[nodiscard]] Quality videoQuality() const {
return _videoQuality;
}
void done(tgcalls::BroadcastPart &&part);
void cancel() override;
@@ -146,6 +160,8 @@ private:
const base::weak_ptr<GroupCall> _call;
const int64 _time = 0;
const int32 _scale = 0;
const int32 _videoChannel = 0;
const Quality _videoQuality = {};
Fn<void(tgcalls::BroadcastPart &&)> _done;
QMutex _mutex;
@@ -379,7 +395,17 @@ GroupCall::LoadPartTask::LoadPartTask(
base::weak_ptr<GroupCall> call,
int64 time,
int64 period,
Fn<void(tgcalls::BroadcastPart &&)> done)
Fn<void(tgcalls::BroadcastPart&&)> done)
: LoadPartTask(std::move(call), time, period, 0, {}, std::move(done)) {
}
GroupCall::LoadPartTask::LoadPartTask(
base::weak_ptr<GroupCall> call,
int64 time,
int64 period,
int32 videoChannel,
tgcalls::VideoChannelDescription::Quality videoQuality,
Fn<void(tgcalls::BroadcastPart&&)> done)
: _call(std::move(call))
, _time(time ? time : (base::unixtime::now() * int64(1000)))
, _scale([&] {
@@ -391,6 +417,8 @@ GroupCall::LoadPartTask::LoadPartTask(
}
Unexpected("Period in LoadPartTask.");
}())
, _videoChannel(videoChannel)
, _videoQuality(videoQuality)
, _done(std::move(done)) {
}
@@ -857,9 +885,6 @@ void GroupCall::setState(State state) {
if (const auto call = _peer->groupCall(); call && call->id() == _id) {
call->setInCall();
}
if (!videoIsWorking()) {
refreshHasNotShownVideo();
}
}
if (false
@@ -1072,9 +1097,6 @@ void GroupCall::markEndpointActive(
bool paused) {
if (!endpoint) {
return;
} else if (active && !videoIsWorking()) {
refreshHasNotShownVideo();
return;
}
const auto i = _activeVideoTracks.find(endpoint);
const auto changed = active
@@ -1715,13 +1737,13 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
void GroupCall::handlePossibleCreateOrJoinResponse(
const MTPDgroupCall &data) {
setScheduledDate(data.vschedule_date().value_or_empty());
if (_acceptFields) {
if (!_instance && !_id) {
const auto input = MTP_inputGroupCall(
data.vid(),
data.vaccess_hash());
const auto scheduleDate = data.vschedule_date().value_or_empty();
setScheduledDate(scheduleDate);
if (const auto chat = _peer->asChat()) {
chat->setGroupCall(input, scheduleDate);
} else if (const auto group = _peer->asChannel()) {
@@ -1735,6 +1757,7 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
} else if (_id != data.vid().v || !_instance) {
return;
}
setScheduledDate(data.vschedule_date().value_or_empty());
if (const auto streamDcId = data.vstream_dc_id()) {
_broadcastDcId = MTP::BareDcId(streamDcId->v);
}
@@ -2189,7 +2212,8 @@ void GroupCall::toggleRecording(bool enabled, const QString &title) {
MTP_flags((enabled ? Flag::f_start : Flag(0))
| (title.isEmpty() ? Flag(0) : Flag::f_title)),
inputCall(),
MTP_string(title)
MTP_string(title),
MTPBool() // video_portrait
)).done([=](const MTPUpdates &result) {
_peer->session().api().applyUpdates(result);
_recordingStoppedByMe = false;
@@ -2233,7 +2257,7 @@ bool GroupCall::tryCreateController() {
.createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator(
settings.callAudioBackend()),
.videoCapture = _cameraCapture,
.requestBroadcastPart = [=, call = base::make_weak(this)](
.requestAudioBroadcastPart = [=, call = base::make_weak(this)](
int64_t time,
int64_t period,
std::function<void(tgcalls::BroadcastPart &&)> done) {
@@ -2247,6 +2271,24 @@ bool GroupCall::tryCreateController() {
});
return result;
},
.requestVideoBroadcastPart = [=, call = base::make_weak(this)](
int64_t time,
int64_t period,
int32_t channel,
tgcalls::VideoChannelDescription::Quality quality,
std::function<void(tgcalls::BroadcastPart &&)> done) {
auto result = std::make_shared<LoadPartTask>(
call,
time,
period,
channel,
quality,
std::move(done));
crl::on_main(weak, [=]() mutable {
broadcastPartStart(std::move(result));
});
return result;
},
.videoContentType = tgcalls::VideoContentType::Generic,
.initialEnableNoiseSuppression
= settings.groupCallNoiseSuppression(),
@@ -2325,17 +2367,30 @@ void GroupCall::broadcastPartStart(std::shared_ptr<LoadPartTask> task) {
const auto raw = task.get();
const auto time = raw->time();
const auto scale = raw->scale();
const auto videoChannel = raw->videoChannel();
const auto videoQuality = raw->videoQuality();
const auto finish = [=](tgcalls::BroadcastPart &&part) {
raw->done(std::move(part));
_broadcastParts.erase(raw);
};
using Status = tgcalls::BroadcastPart::Status;
using Quality = tgcalls::VideoChannelDescription::Quality;
using Flag = MTPDinputGroupCallStream::Flag;
const auto requestId = _api.request(MTPupload_GetFile(
MTP_flags(0),
MTP_inputGroupCallStream(
MTP_flags(videoChannel
? (Flag::f_video_channel | Flag::f_video_quality)
: Flag(0)),
inputCall(),
MTP_long(time),
MTP_int(scale)),
MTP_int(scale),
MTP_int(videoChannel),
MTP_int((videoQuality == Quality::Full)
? 2
: (videoQuality == Quality::Medium)
? 1
: 0)),
MTP_int(0),
MTP_int(128 * 1024)
)).done([=](
@@ -2349,7 +2404,7 @@ void GroupCall::broadcastPartStart(std::shared_ptr<LoadPartTask> task) {
.timestampMilliseconds = time,
.responseTimestamp = TimestampFromMsgId(response.outerMsgId),
.status = Status::Success,
.oggData = std::move(bytes),
.data = std::move(bytes),
});
}, [&](const MTPDupload_fileCdnRedirect &data) {
LOG(("Voice Chat Stream Error: fileCdnRedirect received."));
@@ -2557,22 +2612,6 @@ void GroupCall::updateRequestedVideoChannelsDelayed() {
});
}
void GroupCall::refreshHasNotShownVideo() {
if (!_joinState.ssrc || hasNotShownVideo()) {
return;
}
const auto real = lookupReal();
Assert(real != nullptr);
const auto hasVideo = [&](const Data::GroupCallParticipant &data) {
return (data.peer != _joinAs)
&& (!GetCameraEndpoint(data.videoParams).empty()
|| !GetScreenEndpoint(data.videoParams).empty());
};
_hasNotShownVideo = _joinState.ssrc
&& ranges::any_of(real->participants(), hasVideo);
}
void GroupCall::fillActiveVideoEndpoints() {
const auto real = lookupReal();
Assert(real != nullptr);
@@ -2580,9 +2619,7 @@ void GroupCall::fillActiveVideoEndpoints() {
const auto me = real->participantByPeer(_joinAs);
if (me && me->videoJoined) {
_videoIsWorking = true;
_hasNotShownVideo = false;
} else {
refreshHasNotShownVideo();
_videoIsWorking = false;
toggleVideo(false);
toggleScreenSharing(std::nullopt);
@@ -2610,30 +2647,28 @@ void GroupCall::fillActiveVideoEndpoints() {
}
};
using Type = VideoEndpointType;
if (_videoIsWorking.current()) {
for (const auto &participant : real->participants()) {
const auto camera = GetCameraEndpoint(participant.videoParams);
if (camera != _cameraEndpoint
&& camera != _screenEndpoint
&& participant.peer != _joinAs) {
const auto paused = IsCameraPaused(participant.videoParams);
feedOne({ Type::Camera, participant.peer, camera }, paused);
}
const auto screen = GetScreenEndpoint(participant.videoParams);
if (screen != _cameraEndpoint
&& screen != _screenEndpoint
&& participant.peer != _joinAs) {
const auto paused = IsScreenPaused(participant.videoParams);
feedOne({ Type::Screen, participant.peer, screen }, paused);
}
for (const auto &participant : real->participants()) {
const auto camera = GetCameraEndpoint(participant.videoParams);
if (camera != _cameraEndpoint
&& camera != _screenEndpoint
&& participant.peer != _joinAs) {
const auto paused = IsCameraPaused(participant.videoParams);
feedOne({ Type::Camera, participant.peer, camera }, paused);
}
const auto screen = GetScreenEndpoint(participant.videoParams);
if (screen != _cameraEndpoint
&& screen != _screenEndpoint
&& participant.peer != _joinAs) {
const auto paused = IsScreenPaused(participant.videoParams);
feedOne({ Type::Screen, participant.peer, screen }, paused);
}
feedOne(
{ Type::Camera, _joinAs, cameraSharingEndpoint() },
isCameraPaused());
feedOne(
{ Type::Screen, _joinAs, screenSharingEndpoint() },
isScreenPaused());
}
feedOne(
{ Type::Camera, _joinAs, cameraSharingEndpoint() },
isCameraPaused());
feedOne(
{ Type::Screen, _joinAs, screenSharingEndpoint() },
isScreenPaused());
if (large && !largeFound) {
setVideoEndpointLarge({});
}
@@ -2782,6 +2817,8 @@ void GroupCall::checkLastSpoke() {
|| muted() == MuteState::Active
|| muted() == MuteState::PushToTalk) {
real->applyLastSpoke(ssrc, when, now);
} else {
real->applyLastSpoke(ssrc, { crl::time(), crl::time() }, now);
}
}
_lastSpoke = std::move(list);

View File

@@ -362,12 +362,6 @@ public:
[[nodiscard]] rpl::producer<bool> videoIsWorkingValue() const {
return _videoIsWorking.value();
}
[[nodiscard]] bool hasNotShownVideo() const {
return _hasNotShownVideo.current();
}
[[nodiscard]] rpl::producer<bool> hasNotShownVideoValue() const {
return _hasNotShownVideo.value();
}
void setCurrentAudioDevice(bool input, const QString &deviceId);
[[nodiscard]] bool isSharingScreen() const;
@@ -521,7 +515,6 @@ private:
void updateRequestedVideoChannels();
void updateRequestedVideoChannelsDelayed();
void fillActiveVideoEndpoints();
void refreshHasNotShownVideo();
void editParticipant(
not_null<PeerData*> participantPeer,
@@ -580,7 +573,6 @@ private:
rpl::variable<MuteState> _muted = MuteState::Muted;
rpl::variable<bool> _canManage = false;
rpl::variable<bool> _videoIsWorking = false;
rpl::variable<bool> _hasNotShownVideo = false;
bool _initialMuteStateSent = false;
bool _acceptFields = false;

View File

@@ -46,125 +46,6 @@ constexpr auto kUserpicBlurRadius = 8;
using Row = MembersRow;
[[nodiscard]] int VideoParticipantsLimit(not_null<Main::Session*> session) {
return int(session->account().appConfig().get<double>(
"groupcall_video_participants_max",
30.));
}
void SetupVideoPlaceholder(
not_null<Ui::RpWidget*> widget,
not_null<PeerData*> chat,
int limit) {
struct State {
QImage blurred;
QImage rounded;
InMemoryKey key = {};
std::shared_ptr<Data::CloudImageView> view;
qint64 blurredCacheKey = 0;
};
const auto state = widget->lifetime().make_state<State>();
const auto refreshBlurred = [=] {
const auto key = chat->userpicUniqueKey(state->view);
if (state->key == key && !state->blurred.isNull()) {
return;
}
constexpr auto size = kUserpicSizeForBlur;
state->key = key;
state->blurred = QImage(
QSize(size, size),
QImage::Format_ARGB32_Premultiplied);
{
auto p = Painter(&state->blurred);
auto hq = PainterHighQualityEnabler(p);
chat->paintUserpicSquare(p, state->view, 0, 0, size);
}
state->blurred = Images::BlurLargeImage(
std::move(state->blurred),
kUserpicBlurRadius);
widget->update();
};
const auto refreshRounded = [=](QSize size) {
refreshBlurred();
const auto key = state->blurred.cacheKey();
if (state->rounded.size() == size && state->blurredCacheKey == key) {
return;
}
state->blurredCacheKey = key;
state->rounded = Images::prepare(
state->blurred,
size.width(),
size.width(), // Square
Images::Option::Smooth,
size.width(),
size.height());
{
auto p = QPainter(&state->rounded);
p.fillRect(
0,
0,
size.width(),
size.height(),
QColor(0, 0, 0, Viewport::kShadowMaxAlpha));
}
state->rounded = Images::prepare(
std::move(state->rounded),
size.width(),
size.height(),
(Images::Option::RoundedLarge | Images::Option::RoundedAll),
size.width(),
size.height());
};
chat->loadUserpic();
refreshBlurred();
widget->paintRequest(
) | rpl::start_with_next([=] {
const auto size = QSize(
widget->width(),
widget->height() - st::groupCallVideoSmallSkip);
refreshRounded(size * cIntRetinaFactor());
auto p = QPainter(widget);
const auto inner = QRect(QPoint(), size);
p.drawImage(inner, state->rounded);
st::groupCallPaused.paint(
p,
(size.width() - st::groupCallPaused.width()) / 2,
st::groupCallVideoPlaceholderIconTop,
size.width());
const auto skip = st::groupCallVideoLargeSkip;
p.setPen(st::groupCallVideoTextFg);
const auto text = QRect(
skip,
st::groupCallVideoPlaceholderTextTop,
(size.width() - 2 * skip),
size.height() - st::groupCallVideoPlaceholderTextTop);
p.setFont(st::semiboldFont);
p.drawText(
text,
tr::lng_group_call_limit(tr::now, lt_count, int(limit)),
style::al_top);
}, widget->lifetime());
}
void SetupVideoAboutLimit(
not_null<Ui::RpWidget*> widget,
not_null<Main::Session*> session,
int limit) {
const auto label = Ui::CreateChild<Ui::FlatLabel>(
widget.get(),
tr::lng_group_call_over_limit(lt_count, rpl::single(limit * 1.)),
st::groupCallVideoLimitLabel);
widget->widthValue(
) | rpl::start_with_next([=](int width) {
label->resizeToWidth(width);
label->moveToLeft(0, st::normalFont->height / 3);
widget->resize(width, label->height() + st::normalFont->height);
}, label->lifetime());
}
} // namespace
class Members::Controller final
@@ -246,7 +127,20 @@ private:
not_null<Row*> row,
const std::optional<Data::GroupCallParticipant> &was,
const Data::GroupCallParticipant *participant);
void updateRowInSoundingMap(
not_null<Row*> row,
bool wasSounding,
uint32 wasSsrc,
uint32 wasAdditionalSsrc,
const Data::GroupCallParticipant *participant);
void updateRowInSoundingMap(
not_null<Row*> row,
bool wasSounding,
uint32 wasSsrc,
bool nowSounding,
uint32 nowSsrc);
void removeRow(not_null<Row*> row);
void removeRowFromSoundingMap(not_null<Row*> row);
void updateRowLevel(not_null<Row*> row, float level);
void checkRowPosition(not_null<Row*> row);
[[nodiscard]] bool needToReorder(not_null<Row*> row) const;
@@ -804,14 +698,50 @@ void Members::Controller::updateRow(
: 0;
row->setSkipLevelUpdate(_skipRowLevelUpdate);
row->updateState(participant);
const auto wasNoSounding = _soundingRowBySsrc.empty();
updateRowInSoundingMap(
row,
wasSounding,
wasSsrc,
wasAdditionalSsrc,
participant);
const auto nowNoSounding = _soundingRowBySsrc.empty();
if (wasNoSounding && !nowNoSounding) {
_soundingAnimation.start();
} else if (nowNoSounding && !wasNoSounding) {
_soundingAnimation.stop();
}
delegate()->peerListUpdateRow(row);
}
void Members::Controller::updateRowInSoundingMap(
not_null<Row*> row,
bool wasSounding,
uint32 wasSsrc,
uint32 wasAdditionalSsrc,
const Data::GroupCallParticipant *participant) {
const auto nowSounding = row->sounding();
const auto nowSsrc = participant ? participant->ssrc : 0;
const auto nowAdditionalSsrc = participant
? GetAdditionalAudioSsrc(participant->videoParams)
: 0;
updateRowInSoundingMap(row, wasSounding, wasSsrc, nowSounding, nowSsrc);
updateRowInSoundingMap(
row,
wasSounding,
wasAdditionalSsrc,
nowSounding,
nowAdditionalSsrc);
}
const auto wasNoSounding = _soundingRowBySsrc.empty();
void Members::Controller::updateRowInSoundingMap(
not_null<Row*> row,
bool wasSounding,
uint32 wasSsrc,
bool nowSounding,
uint32 nowSsrc) {
if (wasSsrc == nowSsrc) {
if (nowSsrc && nowSounding != wasSounding) {
if (nowSounding) {
@@ -826,32 +756,14 @@ void Members::Controller::updateRow(
_soundingRowBySsrc.emplace(nowSsrc, row);
}
}
if (wasAdditionalSsrc == nowAdditionalSsrc) {
if (nowAdditionalSsrc && nowSounding != wasSounding) {
if (nowSounding) {
_soundingRowBySsrc.emplace(nowAdditionalSsrc, row);
} else {
_soundingRowBySsrc.remove(nowAdditionalSsrc);
}
}
} else {
_soundingRowBySsrc.remove(wasAdditionalSsrc);
if (nowSounding && nowAdditionalSsrc) {
_soundingRowBySsrc.emplace(nowAdditionalSsrc, row);
}
}
const auto nowNoSounding = _soundingRowBySsrc.empty();
if (wasNoSounding && !nowNoSounding) {
_soundingAnimation.start();
} else if (nowNoSounding && !wasNoSounding) {
_soundingAnimation.stop();
}
delegate()->peerListUpdateRow(row);
}
void Members::Controller::removeRow(not_null<Row*> row) {
removeRowFromSoundingMap(row);
delegate()->peerListRemoveRow(row);
}
void Members::Controller::removeRowFromSoundingMap(not_null<Row*> row) {
// There may be 0, 1 or 2 entries for a row.
for (auto i = begin(_soundingRowBySsrc); i != end(_soundingRowBySsrc);) {
if (i->second == row) {
@@ -860,7 +772,6 @@ void Members::Controller::removeRow(not_null<Row*> row) {
++i;
}
}
delegate()->peerListRemoveRow(row);
}
void Members::Controller::updateRowLevel(
@@ -944,18 +855,22 @@ void Members::Controller::prepareRows(not_null<Data::GroupCall*> real) {
auto changed = false;
auto count = delegate()->peerListFullRowsCount();
for (auto i = 0; i != count;) {
auto row = delegate()->peerListRowAt(i);
auto participantPeer = row->peer();
if (isMe(participantPeer)) {
const auto row = static_cast<Row*>(
delegate()->peerListRowAt(i).get());
removeRowFromSoundingMap(row);
const auto participantPeer = row->peer();
const auto me = isMe(participantPeer);
if (me) {
foundMe = true;
++i;
continue;
}
if (real->participantByPeer(participantPeer)) {
if (const auto found = real->participantByPeer(participantPeer)) {
updateRowInSoundingMap(row, false, 0, 0, found);
++i;
} else if (me) {
++i;
} else {
changed = true;
removeRow(static_cast<Row*>(row.get()));
removeRow(row);
--count;
}
}
@@ -1624,8 +1539,6 @@ Members::Members(
, _layout(_scroll->setOwnedWidget(
object_ptr<Ui::VerticalLayout>(_scroll.data())))
, _videoWrap(_layout->add(object_ptr<Ui::RpWidget>(_layout.get())))
, _videoPlaceholder(std::make_unique<Ui::RpWidget>(_videoWrap.get()))
, _videoAboutLimit(std::make_unique<Ui::RpWidget>(_videoWrap.get()))
, _viewport(
std::make_unique<Viewport>(
_videoWrap.get(),
@@ -1863,61 +1776,9 @@ void Members::trackViewportGeometry() {
_scroll->scrollTopValue(
) | rpl::skip(1) | rpl::start_with_next(move, _viewport->lifetime());
const auto videoLimit = VideoParticipantsLimit(&_call->peer()->session());
rpl::combine(
_layout->widthValue(),
_call->hasNotShownVideoValue()
) | rpl::start_with_next([=](int width, bool has) {
const auto height = has ? st::groupCallVideoPlaceholderHeight : 0;
_videoPlaceholder->setGeometry(0, 0, width, height);
}, _videoPlaceholder->lifetime());
SetupVideoPlaceholder(_videoPlaceholder.get(), _call->peer(), videoLimit);
_layout->widthValue(
) | rpl::start_with_next([=](int width) {
_videoAboutLimit->resizeToWidth(width);
}, _videoAboutLimit->lifetime());
using namespace rpl::mappers;
auto aboutLimitRelevant = fullCountValue(
) | rpl::map(
_1 > videoLimit
) | rpl::distinct_until_changed();
auto aboutLimitShown = rpl::combine(
std::move(aboutLimitRelevant),
_call->canManageValue(),
_1 && _2);
SetupVideoAboutLimit(
_videoAboutLimit.get(),
&_call->peer()->session(),
videoLimit);
rpl::combine(
_videoPlaceholder->heightValue(),
_viewport->fullHeightValue(),
_videoAboutLimit->heightValue(),
std::move(aboutLimitShown)
) | rpl::start_with_next([=](
int placeholder,
int viewport,
int aboutLimit,
bool aboutLimitShown) {
if (placeholder > 0 || viewport <= 0 || !aboutLimitShown) {
aboutLimitShown = false;
}
// This call may update _videoAboutLimit->height() :(
_videoAboutLimit->setVisible(aboutLimitShown);
_videoAboutLimit->move(0, viewport);
_videoWrap->resize(
_videoWrap->width(),
std::max(
placeholder,
(viewport
+ (aboutLimitShown ? _videoAboutLimit->height() : 0))));
_viewport->fullHeightValue(
) | rpl::start_with_next([=](int viewport) {
_videoWrap->resize(_videoWrap->width(), viewport);
if (viewport > 0) {
move();
resize();

View File

@@ -101,8 +101,6 @@ private:
std::unique_ptr<Controller> _listController;
not_null<Ui::VerticalLayout*> _layout;
const not_null<Ui::RpWidget*> _videoWrap;
const std::unique_ptr<Ui::RpWidget> _videoPlaceholder;
const std::unique_ptr<Ui::RpWidget> _videoAboutLimit;
std::unique_ptr<Viewport> _viewport;
rpl::variable<Ui::RpWidget*> _addMemberButton = nullptr;
RpWidget *_topSkip = nullptr;

View File

@@ -48,7 +48,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "base/qt_signal_producer.h"
#include "base/timer_rpl.h"
#include "app.h"
#include "apiwrap.h" // api().kickParticipant.
#include "webrtc/webrtc_video_track.h"
#include "webrtc/webrtc_media_devices.h" // UniqueDesktopCaptureSource.

View File

@@ -33,7 +33,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "window/window_session_controller.h"
#include "history/view/history_view_cursor_state.h"
#include "app.h"
#include "storage/storage_account.h" // Account::writeSavedGifs
#include "styles/style_chat_helpers.h"

View File

@@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h"
#include "inline_bots/inline_bot_layout_item.h"
#include "layout/layout_mosaic.h"
#include "app.h"
#include <QtCore/QTimer>

View File

@@ -14,6 +14,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_schedule_box.h"
#include "lang/lang_keys.h"
#include "ui/widgets/popup_menu.h"
#include "data/data_peer.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include <QtWidgets/QApplication>
@@ -133,4 +136,45 @@ void SetupMenuAndShortcuts(
}, button->lifetime());
}
void SetupUnreadMentionsMenu(
not_null<Ui::RpWidget*> button,
Fn<PeerData*()> currentPeer) {
struct State {
base::unique_qptr<Ui::PopupMenu> menu;
base::flat_set<not_null<PeerData*>> sentForPeers;
};
const auto state = std::make_shared<State>();
const auto showMenu = [=] {
const auto peer = currentPeer();
if (!peer) {
return;
}
state->menu = base::make_unique_q<Ui::PopupMenu>(button);
const auto text = tr::lng_context_mark_read_mentions_all(tr::now);
state->menu->addAction(text, [=] {
if (!state->sentForPeers.emplace(peer).second) {
return;
}
peer->session().api().request(MTPmessages_ReadMentions(
peer->input
)).done([=](const MTPmessages_AffectedHistory &result) {
state->sentForPeers.remove(peer);
peer->session().api().applyAffectedHistory(peer, result);
}).fail([=](const MTP::Error &error) {
state->sentForPeers.remove(peer);
}).send();
});
state->menu->popup(QCursor::pos());
};
base::install_event_filter(button, [=](not_null<QEvent*> e) {
if (e->type() == QEvent::ContextMenu) {
showMenu();
return base::EventFilterResult::Cancel;
}
return base::EventFilterResult::Continue;
});
}
} // namespace SendMenu

View File

@@ -50,4 +50,8 @@ void SetupMenuAndShortcuts(
Fn<void()> silent,
Fn<void()> schedule);
void SetupUnreadMentionsMenu(
not_null<Ui::RpWidget*> button,
Fn<PeerData*()> currentPeer);
} // namespace SendMenu

View File

@@ -21,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "base/call_delayed.h"
#include "apiwrap.h"
#include "app.h"
#include "styles/style_chat.h"
#include <QtCore/QBuffer>

View File

@@ -970,6 +970,11 @@ StickersListWidget::StickersListWidget(
}
refreshRecent();
}, lifetime());
positionValue(
) | rpl::skip(1) | rpl::map_to(
TabbedSelector::Action::Update
) | rpl::start_to_stream(_choosingUpdated, lifetime());
}
Main::Session &StickersListWidget::session() const {
@@ -988,6 +993,11 @@ rpl::producer<> StickersListWidget::checkForHide() const {
return _checkForHide.events();
}
auto StickersListWidget::choosingUpdated() const
-> rpl::producer<TabbedSelector::Action> {
return _choosingUpdated.events();
}
object_ptr<TabbedSelector::InnerFooter> StickersListWidget::createFooter() {
Expects(_footer == nullptr);
@@ -2361,6 +2371,7 @@ TabbedSelector::InnerFooter *StickersListWidget::getFooter() const {
}
void StickersListWidget::processHideFinished() {
_choosingUpdated.fire(TabbedSelector::Action::Cancel);
clearSelection();
clearHeavyData();
if (_footer) {

View File

@@ -55,6 +55,7 @@ public:
rpl::producer<TabbedSelector::FileChosen> chosen() const;
rpl::producer<> scrollUpdated() const;
rpl::producer<> checkForHide() const;
rpl::producer<TabbedSelector::Action> choosingUpdated() const;
void refreshRecent() override;
void preloadImages() override;
@@ -392,6 +393,7 @@ private:
rpl::event_stream<TabbedSelector::FileChosen> _chosen;
rpl::event_stream<> _scrollUpdated;
rpl::event_stream<> _checkForHide;
rpl::event_stream<TabbedSelector::Action> _choosingUpdated;
};

View File

@@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "mainwindow.h"
#include "core/application.h"
#include "app.h"
#include "styles/style_chat_helpers.h"
namespace ChatHelpers {

View File

@@ -479,6 +479,13 @@ auto TabbedSelector::inlineResultChosen() const
return hasGifsTab() ? gifs()->inlineResultChosen() : nullptr;
}
auto TabbedSelector::choosingStickerUpdated() const
-> rpl::producer<TabbedSelector::Action>{
return hasStickersTab()
? stickers()->choosingUpdated()
: rpl::never<Action>();
}
rpl::producer<> TabbedSelector::cancelled() const {
return hasGifsTab() ? gifs()->cancelRequests() : nullptr;
}

View File

@@ -67,6 +67,10 @@ public:
EmojiOnly,
MediaEditor,
};
enum class Action {
Update,
Cancel,
};
TabbedSelector(
QWidget *parent,
@@ -85,6 +89,7 @@ public:
rpl::producer<> checkForHide() const;
rpl::producer<> slideFinished() const;
rpl::producer<> contextMenuRequested() const;
rpl::producer<Action> choosingStickerUpdated() const;
void setRoundRadius(int radius);
void refreshStickers();

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "core/application.h"
#include "data/data_abstract_structure.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "data/data_session.h"
@@ -34,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "api/api_updates.h"
#include "calls/calls_instance.h"
#include "countries/countries_manager.h"
#include "lang/lang_file_parser.h"
#include "lang/lang_translator.h"
#include "lang/lang_cloud_manager.h"
@@ -41,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_instance.h"
#include "mainwidget.h"
#include "core/file_utilities.h"
#include "core/crash_reports.h"
#include "main/main_account.h"
#include "main/main_domain.h"
#include "main/main_session.h"
@@ -64,6 +67,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text_options.h"
#include "ui/emoji_config.h"
#include "ui/effects/animations.h"
#include "ui/cached_round_corners.h"
#include "storage/serialize_common.h"
#include "storage/storage_domain.h"
#include "storage/storage_databases.h"
@@ -92,6 +96,27 @@ constexpr auto kQuitPreventTimeoutMs = crl::time(1500);
constexpr auto kAutoLockTimeoutLateMs = crl::time(3000);
constexpr auto kClearEmojiImageSourceTimeout = 10 * crl::time(1000);
void SetCrashAnnotationsGL() {
#ifdef Q_OS_WIN
CrashReports::SetAnnotation("OpenGL ANGLE", [] {
if (Core::App().settings().disableOpenGL()) {
return "Disabled";
} else switch (Ui::GL::CurrentANGLE()) {
case Ui::GL::ANGLE::Auto: return "Auto";
case Ui::GL::ANGLE::D3D11: return "Direct3D 11";
case Ui::GL::ANGLE::D3D9: return "Direct3D 9";
case Ui::GL::ANGLE::D3D11on12: return "D3D11on12";
case Ui::GL::ANGLE::OpenGL: return "OpenGL";
}
Unexpected("Ui::GL::CurrentANGLE value in SetupANGLE.");
}());
#else // Q_OS_WIN
CrashReports::SetAnnotation(
"OpenGL",
Core::App().settings().disableOpenGL() ? "Disabled" : "Enabled");
#endif // Q_OS_WIN
}
} // namespace
Application *Application::Instance = nullptr;
@@ -172,7 +197,8 @@ Application::~Application() {
Ui::Emoji::Clear();
Media::Clip::Finish();
App::deinitMedia();
Ui::FinishCachedCorners();
Data::clearGlobalStructures();
Window::Theme::Uninitialize();
@@ -217,6 +243,7 @@ void Application::run() {
style::startManager(cScale());
Ui::InitTextOptions();
Ui::StartCachedCorners();
Ui::Emoji::Init();
startEmojiImageLoader();
startSystemDarkModeViewer();
@@ -262,7 +289,6 @@ void Application::run() {
// Depend on activeWindow() for now :(
startShortcuts();
App::initMedia();
startDomain();
_window->widget()->show();
@@ -284,6 +310,7 @@ void Application::run() {
LOG(("Shortcuts Error: %1").arg(error));
}
SetCrashAnnotationsGL();
if (!Platform::IsMac() && Ui::GL::LastCrashCheckFailed()) {
showOpenGLCrashNotification();
}
@@ -294,6 +321,14 @@ void Application::run() {
_mediaView->show(std::move(request));
}
}, _window->lifetime());
{
const auto countries = std::make_shared<Countries::Manager>(
_domain.get());
countries->lifetime().add([=] {
[[maybe_unused]] const auto countriesCopy = countries;
});
}
}
void Application::showOpenGLCrashNotification() {

View File

@@ -88,6 +88,26 @@ std::map<int, const char*> BetaLogs() {
"- Fix Direct3D acceleration on basic Windows 7 setup.\n"
},
{
2009004,
"- Choose one from dozens of new gorgeous animated backgrounds"
" in Chat Settings > Chat background.\n"
},
{
2009005,
"- Tile chat background patterns horizontally.\n"
"- Fix a rare crash in spellchecker on Windows.\n"
"- Fix animated chat backgrounds in Saved Messages.\n"
"- Fix \"Sorry, group is inaccessible\" message "
"in scheduled voice chats.\n",
},
{
2009013,
"- See unread comments count when scrolling discussions in channels."
},
};
};

View File

@@ -23,7 +23,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "window/window_session_controller.h"
#include "facades.h"
#include "app.h"
#include <QtGui/QGuiApplication>

View File

@@ -299,10 +299,8 @@ QString PlatformString() {
return qsl("MacAppStore");
} else if (Platform::IsMac()) {
return qsl("MacOS");
} else if (Platform::IsLinux32Bit()) {
return qsl("Linux32Bit");
} else if (Platform::IsLinux64Bit()) {
return qsl("Linux64bit");
} else if (Platform::IsLinux()) {
return qsl("Linux");
}
Unexpected("Platform in CrashReports::PlatformString.");
}

View File

@@ -270,12 +270,12 @@ std::unique_ptr<Launcher> Launcher::Create(int argc, char *argv[]) {
return std::make_unique<Platform::Launcher>(argc, argv);
}
Launcher::Launcher(
int argc,
char *argv[])
Launcher::Launcher(int argc, char *argv[])
: _argc(argc)
, _argv(argv)
, _baseIntegration(_argc, _argv) {
crl::toggle_fp_exceptions(true);
base::Integration::Set(&_baseIntegration);
}
@@ -286,10 +286,7 @@ void Launcher::init() {
initQtMessageLogging();
QApplication::setApplicationName(qsl("TelegramDesktop"));
#ifndef OS_MAC_OLD
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling, true);
#endif // OS_MAC_OLD
// fallback session management is useless for tdesktop since it doesn't have
// any "are you sure you want to close this window?" dialogs
@@ -427,9 +424,17 @@ void Launcher::initQtMessageLogging() {
QtMsgType type,
const QMessageLogContext &context,
const QString &msg) {
if (OriginalMessageHandler) {
OriginalMessageHandler(type, context, msg);
}
const auto InvokeOriginal = [&] {
#ifndef _DEBUG
if (Logs::DebugEnabled()) {
return;
}
#endif // _DEBUG
if (OriginalMessageHandler) {
OriginalMessageHandler(type, context, msg);
}
};
InvokeOriginal();
if (Logs::DebugEnabled() || !Logs::started()) {
if (!Logs::WritingEntry()) {
// Sometimes Qt logs something inside our own logging.
@@ -450,7 +455,6 @@ void Launcher::processArguments() {
AllLeftValues,
};
auto parseMap = std::map<QByteArray, KeyFormat> {
{ "-testmode" , KeyFormat::NoValues },
{ "-debug" , KeyFormat::NoValues },
{ "-freetype" , KeyFormat::NoValues },
{ "-many" , KeyFormat::NoValues },
@@ -459,7 +463,6 @@ void Launcher::processArguments() {
{ "-fixprevious" , KeyFormat::NoValues },
{ "-cleanup" , KeyFormat::NoValues },
{ "-noupdate" , KeyFormat::NoValues },
{ "-externalupdater", KeyFormat::NoValues },
{ "-tosettings" , KeyFormat::NoValues },
{ "-startintray" , KeyFormat::NoValues },
{ "-sendpath" , KeyFormat::AllLeftValues },
@@ -490,9 +493,6 @@ void Launcher::processArguments() {
}
}
if (parseResult.contains("-externalupdater")) {
SetUpdaterDisabledAtStartup();
}
gUseFreeType = parseResult.contains("-freetype");
gDebugMode = parseResult.contains("-debug");
gManyInstance = parseResult.contains("-many");

View File

@@ -13,9 +13,7 @@ namespace Core {
class Launcher {
public:
Launcher(
int argc,
char *argv[]);
Launcher(int argc, char *argv[]);
static std::unique_ptr<Launcher> Create(int argc, char *argv[]);

View File

@@ -40,7 +40,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "apiwrap.h"
#include "app.h"
#include <QtGui/QGuiApplication>
@@ -232,15 +231,16 @@ bool ShowWallPaper(
const auto params = url_parse_params(
match->captured(1),
qthelp::UrlParamNameTransform::ToLower);
if (!params.value("gradient").isEmpty()) {
Ui::show(Box<InformBox>(
tr::lng_background_gradient_unsupported(tr::now)));
return false;
}
const auto bg = params.value("bg_color");
const auto color = params.value("color");
const auto gradient = params.value("gradient");
return BackgroundPreviewBox::Start(
controller,
(color.isEmpty() ? params.value(qsl("slug")) : color),
(!color.isEmpty()
? color
: !gradient.isEmpty()
? gradient
: params.value(qsl("slug"))),
params);
}

View File

@@ -21,6 +21,7 @@ QStringList MimeType::globPatterns() const {
switch (_type) {
case Known::WebP: return QStringList(u"*.webp"_q);
case Known::Tgs: return QStringList(u"*.tgs"_q);
case Known::Tgv: return QStringList(u"*.tgv"_q);
case Known::TDesktopTheme: return QStringList(u"*.tdesktop-theme"_q);
case Known::TDesktopPalette: return QStringList(u"*.tdesktop-palette"_q);
default: break;
@@ -32,6 +33,7 @@ QString MimeType::filterString() const {
switch (_type) {
case Known::WebP: return u"WebP image (*.webp)"_q;
case Known::Tgs: return u"Telegram sticker (*.tgs)"_q;
case Known::Tgv: return u"Wallpaper pattern (*.tgv)"_q;
case Known::TDesktopTheme: return u"Theme files (*.tdesktop-theme)"_q;
case Known::TDesktopPalette: return u"Palette files (*.tdesktop-palette)"_q;
default: break;
@@ -43,6 +45,7 @@ QString MimeType::name() const {
switch (_type) {
case Known::WebP: return u"image/webp"_q;
case Known::Tgs: return u"application/x-tgsticker"_q;
case Known::Tgv: return u"application/x-tgwallpattern"_q;
case Known::TDesktopTheme: return u"application/x-tdesktop-theme"_q;
case Known::TDesktopPalette: return u"application/x-tdesktop-palette"_q;
default: break;
@@ -55,6 +58,8 @@ MimeType MimeTypeForName(const QString &mime) {
return MimeType(MimeType::Known::WebP);
} else if (mime == qstr("application/x-tgsticker")) {
return MimeType(MimeType::Known::Tgs);
} else if (mime == qstr("application/x-tgwallpattern")) {
return MimeType(MimeType::Known::Tgv);
} else if (mime == qstr("application/x-tdesktop-theme")
|| mime == qstr("application/x-tgtheme-tdesktop")) {
return MimeType(MimeType::Known::TDesktopTheme);
@@ -72,6 +77,8 @@ MimeType MimeTypeForFile(const QFileInfo &file) {
return MimeType(MimeType::Known::WebP);
} else if (path.endsWith(qstr(".tgs"), Qt::CaseInsensitive)) {
return MimeType(MimeType::Known::Tgs);
} else if (path.endsWith(qstr(".tgv"))) {
return MimeType(MimeType::Known::Tgv);
} else if (path.endsWith(qstr(".tdesktop-theme"), Qt::CaseInsensitive)) {
return MimeType(MimeType::Known::TDesktopTheme);
} else if (path.endsWith(qstr(".tdesktop-palette"), Qt::CaseInsensitive)) {

View File

@@ -22,6 +22,7 @@ public:
TDesktopPalette,
WebP,
Tgs,
Tgv,
};
explicit MimeType(const QMimeType &type);

View File

@@ -242,9 +242,7 @@ QString FindUpdateFile() {
"tupdate|"
"tx64upd|"
"tmacupd|"
"tosxupd|"
"tlinuxupd|"
"tlinux32upd"
")\\d+(_[a-z\\d]+)?$",
QRegularExpression::CaseInsensitiveOption
).match(info.fileName()).hasMatch()) {

View File

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

View File

@@ -0,0 +1,460 @@
/*
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 "countries/countries_instance.h"
namespace Countries {
namespace {
auto SingleInstance = CountriesInstance();
const std::array<Info, 231> FallbackList = { {
{ "Andorra", "AD", "", { CallingCodeInfo{ "376", {}, { "XX XX XX" } } }, false },
{ "United Arab Emirates", "AE", "", { CallingCodeInfo{ "971", {}, { "XX XXX XXXX" } } }, false },
{ "Afghanistan", "AF", "", { CallingCodeInfo{ "93", {}, { "XXX XXX XXX" } } }, false },
{ "Antigua & Barbuda", "AG", "", { CallingCodeInfo{ "1268", {}, { "XXX XXXX" } } }, false },
{ "Anguilla", "AI", "", { CallingCodeInfo{ "1264", {}, { "XXX XXXX" } } }, false },
{ "Albania", "AL", "", { CallingCodeInfo{ "355", {}, { "XX XXX XXXX" } } }, false },
{ "Armenia", "AM", "", { CallingCodeInfo{ "374", {}, { "XX XXX XXX" } } }, false },
{ "Angola", "AO", "", { CallingCodeInfo{ "244", {}, { "XXX XXX XXX" } } }, false },
{ "Argentina", "AR", "", { CallingCodeInfo{ "54", {}, {} } }, false },
{ "American Samoa", "AS", "", { CallingCodeInfo{ "1684", {}, { "XXX XXXX" } } }, false },
{ "Austria", "AT", "", { CallingCodeInfo{ "43", {}, { "X XXXXXXXX" } } }, false },
{ "Australia", "AU", "", { CallingCodeInfo{ "61", {}, { "X XXXX XXXX" } } }, false },
{ "Aruba", "AW", "", { CallingCodeInfo{ "297", {}, { "XXX XXXX" } } }, false },
{ "Azerbaijan", "AZ", "", { CallingCodeInfo{ "994", {}, { "XX XXX XXXX" } } }, false },
{ "Bosnia & Herzegovina", "BA", "", { CallingCodeInfo{ "387", {}, { "XX XXX XXX" } } }, false },
{ "Barbados", "BB", "", { CallingCodeInfo{ "1246", {}, { "XXX XXXX" } } }, false },
{ "Bangladesh", "BD", "", { CallingCodeInfo{ "880", {}, { "XX XXX XXX" } } }, false },
{ "Belgium", "BE", "", { CallingCodeInfo{ "32", {}, { "XXX XX XX XX" } } }, false },
{ "Burkina Faso", "BF", "", { CallingCodeInfo{ "226", {}, { "XX XX XX XX" } } }, false },
{ "Bulgaria", "BG", "", { CallingCodeInfo{ "359", {}, {} } }, false },
{ "Bahrain", "BH", "", { CallingCodeInfo{ "973", {}, { "XXXX XXXX" } } }, false },
{ "Burundi", "BI", "", { CallingCodeInfo{ "257", {}, { "XX XX XXXX" } } }, false },
{ "Benin", "BJ", "", { CallingCodeInfo{ "229", {}, { "XX XXX XXX" } } }, false },
{ "Bermuda", "BM", "", { CallingCodeInfo{ "1441", {}, { "XXX XXXX" } } }, false },
{ "Brunei Darussalam", "BN", "", { CallingCodeInfo{ "673", {}, { "XXX XXXX" } } }, false },
{ "Bolivia", "BO", "", { CallingCodeInfo{ "591", {}, { "X XXX XXXX" } } }, false },
{ "Bonaire, Sint Eustatius & Saba", "BQ", "", { CallingCodeInfo{ "599", {}, {} } }, false },
{ "Brazil", "BR", "", { CallingCodeInfo{ "55", {}, { "XX XXXXX XXXX" } } }, false },
{ "Bahamas", "BS", "", { CallingCodeInfo{ "1242", {}, { "XXX XXXX" } } }, false },
{ "Bhutan", "BT", "", { CallingCodeInfo{ "975", {}, { "XX XXX XXX" } } }, false },
{ "Botswana", "BW", "", { CallingCodeInfo{ "267", {}, { "XX XXX XXX" } } }, false },
{ "Belarus", "BY", "", { CallingCodeInfo{ "375", {}, { "XX XXX XXXX" } } }, false },
{ "Belize", "BZ", "", { CallingCodeInfo{ "501", {}, {} } }, false },
{ "Canada", "CA", "", { CallingCodeInfo{ "1", { "403" }, { "XXX XXX XXXX" } } }, false },
{ "Congo (Dem. Rep.)", "CD", "", { CallingCodeInfo{ "243", {}, { "XX XXX XXXX" } } }, false },
{ "Central African Rep.", "CF", "", { CallingCodeInfo{ "236", {}, { "XX XX XX XX" } } }, false },
{ "Congo (Rep.)", "CG", "", { CallingCodeInfo{ "242", {}, { "XX XXX XXXX" } } }, false },
{ "Switzerland", "CH", "", { CallingCodeInfo{ "41", {}, { "XX XXX XXXX" } } }, false },
{ "Côte d'Ivoire", "CI", "", { CallingCodeInfo{ "225", {}, { "XX XX XX XXXX" } } }, false },
{ "Cook Islands", "CK", "", { CallingCodeInfo{ "682", {}, {} } }, false },
{ "Chile", "CL", "", { CallingCodeInfo{ "56", {}, { "X XXXX XXXX" } } }, false },
{ "Cameroon", "CM", "", { CallingCodeInfo{ "237", {}, { "XXXX XXXX" } } }, false },
{ "China", "CN", "", { CallingCodeInfo{ "86", {}, { "XXX XXXX XXXX" } } }, false },
{ "Colombia", "CO", "", { CallingCodeInfo{ "57", {}, { "XXX XXX XXXX" } } }, false },
{ "Costa Rica", "CR", "", { CallingCodeInfo{ "506", {}, { "XXXX XXXX" } } }, false },
{ "Cuba", "CU", "", { CallingCodeInfo{ "53", {}, { "X XXX XXXX" } } }, false },
{ "Cape Verde", "CV", "", { CallingCodeInfo{ "238", {}, { "XXX XXXX" } } }, false },
{ "Curaçao", "CW", "", { CallingCodeInfo{ "599", { "9" }, {} } }, false },
{ "Cyprus", "CY", "", { CallingCodeInfo{ "357", {}, { "XXXX XXXX" } } }, false },
{ "Czech Republic", "CZ", "", { CallingCodeInfo{ "420", {}, { "XXX XXX XXX" } } }, false },
{ "Germany", "DE", "", { CallingCodeInfo{ "49", {}, { "XXXX XXXXXXX" } } }, false },
{ "Djibouti", "DJ", "", { CallingCodeInfo{ "253", {}, { "XX XX XX XX" } } }, false },
{ "Denmark", "DK", "", { CallingCodeInfo{ "45", {}, { "XXXX XXXX" } } }, false },
{ "Dominica", "DM", "", { CallingCodeInfo{ "1767", {}, { "XXX XXXX" } } }, false },
{ "Dominican Rep.", "DO", "", { CallingCodeInfo{ "1809", {}, { "XXX XXXX" } } }, false },
{ "Algeria", "DZ", "", { CallingCodeInfo{ "213", {}, { "XXX XX XX XX" } } }, false },
{ "Ecuador", "EC", "", { CallingCodeInfo{ "593", {}, { "XX XXX XXXX" } } }, false },
{ "Estonia", "EE", "", { CallingCodeInfo{ "372", {}, { "XXXX XXXX" } } }, false },
{ "Egypt", "EG", "", { CallingCodeInfo{ "20", {}, { "XX XXXX XXXX" } } }, false },
{ "Eritrea", "ER", "", { CallingCodeInfo{ "291", {}, { "X XXX XXX" } } }, false },
{ "Spain", "ES", "", { CallingCodeInfo{ "34", {}, { "XXX XXX XXX" } } }, false },
{ "Ethiopia", "ET", "", { CallingCodeInfo{ "251", {}, { "XX XXX XXXX" } } }, false },
{ "Finland", "FI", "", { CallingCodeInfo{ "358", {}, {} } }, false },
{ "Fiji", "FJ", "", { CallingCodeInfo{ "679", {}, { "XXX XXXX" } } }, false },
{ "Falkland Islands", "FK", "", { CallingCodeInfo{ "500", {}, {} } }, false },
{ "Micronesia", "FM", "", { CallingCodeInfo{ "691", {}, {} } }, false },
{ "Faroe Islands", "FO", "", { CallingCodeInfo{ "298", {}, { "XXX XXX" } } }, false },
{ "France", "FR", "", { CallingCodeInfo{ "33", {}, { "X XX XX XX XX" } } }, false },
{ "Gabon", "GA", "", { CallingCodeInfo{ "241", {}, { "X XX XX XX" } } }, false },
{ "United Kingdom", "GB", "", { CallingCodeInfo{ "44", {}, { "XXXX XXXXXX" } } }, false },
{ "Grenada", "GD", "", { CallingCodeInfo{ "1473", {}, { "XXX XXXX" } } }, false },
{ "Georgia", "GE", "", { CallingCodeInfo{ "995", {}, { "XXX XXX XXX" } } }, false },
{ "French Guiana", "GF", "", { CallingCodeInfo{ "594", {}, {} } }, false },
{ "Ghana", "GH", "", { CallingCodeInfo{ "233", {}, { "XX XXX XXXX" } } }, false },
{ "Gibraltar", "GI", "", { CallingCodeInfo{ "350", {}, { "XXXX XXXX" } } }, false },
{ "Greenland", "GL", "", { CallingCodeInfo{ "299", {}, { "XXX XXX" } } }, false },
{ "Gambia", "GM", "", { CallingCodeInfo{ "220", {}, { "XXX XXXX" } } }, false },
{ "Guinea", "GN", "", { CallingCodeInfo{ "224", {}, { "XXX XXX XXX" } } }, false },
{ "Guadeloupe", "GP", "", { CallingCodeInfo{ "590", {}, { "XXX XX XX XX" } } }, false },
{ "Equatorial Guinea", "GQ", "", { CallingCodeInfo{ "240", {}, { "XXX XXX XXX" } } }, false },
{ "Greece", "GR", "", { CallingCodeInfo{ "30", {}, { "XXX XXX XXXX" } } }, false },
{ "Guatemala", "GT", "", { CallingCodeInfo{ "502", {}, { "X XXX XXXX" } } }, false },
{ "Guam", "GU", "", { CallingCodeInfo{ "1671", {}, { "XXX XXXX" } } }, false },
{ "Guinea-Bissau", "GW", "", { CallingCodeInfo{ "245", {}, { "XXX XXXX" } } }, false },
{ "Guyana", "GY", "", { CallingCodeInfo{ "592", {}, {} } }, false },
{ "Hong Kong", "HK", "", { CallingCodeInfo{ "852", {}, { "X XXX XXXX" } } }, false },
{ "Honduras", "HN", "", { CallingCodeInfo{ "504", {}, { "XXXX XXXX" } } }, false },
{ "Croatia", "HR", "", { CallingCodeInfo{ "385", {}, { "XX XXX XXX" } } }, false },
{ "Haiti", "HT", "", { CallingCodeInfo{ "509", {}, { "XXXX XXXX" } } }, false },
{ "Hungary", "HU", "", { CallingCodeInfo{ "36", {}, { "XXX XXX XXX" } } }, false },
{ "Indonesia", "ID", "", { CallingCodeInfo{ "62", {}, { "XXX XXXXXX" } } }, false },
{ "Ireland", "IE", "", { CallingCodeInfo{ "353", {}, { "XX XXX XXXX" } } }, false },
{ "Israel", "IL", "", { CallingCodeInfo{ "972", {}, { "XX XXX XXXX" } } }, false },
{ "India", "IN", "", { CallingCodeInfo{ "91", {}, { "XXXXX XXXXX" } } }, false },
{ "Diego Garcia", "IO", "", { CallingCodeInfo{ "246", {}, { "XXX XXXX" } } }, false },
{ "Iraq", "IQ", "", { CallingCodeInfo{ "964", {}, { "XXX XXX XXXX" } } }, false },
{ "Iran", "IR", "", { CallingCodeInfo{ "98", {}, { "XXX XXX XXXX" } } }, false },
{ "Iceland", "IS", "", { CallingCodeInfo{ "354", {}, { "XXX XXXX" } } }, false },
{ "Italy", "IT", "", { CallingCodeInfo{ "39", {}, { "XXX XXX XXX" } } }, false },
{ "Jamaica", "JM", "", { CallingCodeInfo{ "1876", {}, { "XXX XXXX" } } }, false },
{ "Jordan", "JO", "", { CallingCodeInfo{ "962", {}, { "X XXXX XXXX" } } }, false },
{ "Japan", "JP", "", { CallingCodeInfo{ "81", {}, { "XX XXXX XXXX" } } }, false },
{ "Kenya", "KE", "", { CallingCodeInfo{ "254", {}, { "XXX XXX XXX" } } }, false },
{ "Kyrgyzstan", "KG", "", { CallingCodeInfo{ "996", {}, { "XXX XXXXXX" } } }, false },
{ "Cambodia", "KH", "", { CallingCodeInfo{ "855", {}, { "XX XXX XXX" } } }, false },
{ "Kiribati", "KI", "", { CallingCodeInfo{ "686", {}, { "XXXX XXXX" } } }, false },
{ "Comoros", "KM", "", { CallingCodeInfo{ "269", {}, { "XXX XXXX" } } }, false },
{ "Saint Kitts & Nevis", "KN", "", { CallingCodeInfo{ "1869", {}, { "XXX XXXX" } } }, false },
{ "North Korea", "KP", "", { CallingCodeInfo{ "850", {}, {} } }, false },
{ "South Korea", "KR", "", { CallingCodeInfo{ "82", {}, { "XX XXXX XXX" } } }, false },
{ "Kuwait", "KW", "", { CallingCodeInfo{ "965", {}, { "XXXX XXXX" } } }, false },
{ "Cayman Islands", "KY", "", { CallingCodeInfo{ "1345", {}, { "XXX XXXX" } } }, false },
{ "Kazakhstan", "KZ", "", { CallingCodeInfo{ "7", { "6" }, { "XXX XXX XX XX" } } }, false },
{ "Laos", "LA", "", { CallingCodeInfo{ "856", {}, { "XX XX XXX XXX" } } }, false },
{ "Lebanon", "LB", "", { CallingCodeInfo{ "961", {}, { "XX XXX XXX" } } }, false },
{ "Saint Lucia", "LC", "", { CallingCodeInfo{ "1758", {}, { "XXX XXXX" } } }, false },
{ "Liechtenstein", "LI", "", { CallingCodeInfo{ "423", {}, { "XXX XXXX" } } }, false },
{ "Sri Lanka", "LK", "", { CallingCodeInfo{ "94", {}, { "XX XXX XXXX" } } }, false },
{ "Liberia", "LR", "", { CallingCodeInfo{ "231", {}, { "XX XXX XXXX" } } }, false },
{ "Lesotho", "LS", "", { CallingCodeInfo{ "266", {}, { "XX XXX XXX" } } }, false },
{ "Lithuania", "LT", "", { CallingCodeInfo{ "370", {}, { "XXX XXXXX" } } }, false },
{ "Luxembourg", "LU", "", { CallingCodeInfo{ "352", {}, { "XXX XXX XXX" } } }, false },
{ "Latvia", "LV", "", { CallingCodeInfo{ "371", {}, { "XXX XXXXX" } } }, false },
{ "Libya", "LY", "", { CallingCodeInfo{ "218", {}, { "XX XXX XXXX" } } }, false },
{ "Morocco", "MA", "", { CallingCodeInfo{ "212", {}, { "XX XXX XXXX" } } }, false },
{ "Monaco", "MC", "", { CallingCodeInfo{ "377", {}, { "XXXX XXXX" } } }, false },
{ "Moldova", "MD", "", { CallingCodeInfo{ "373", {}, { "XX XXX XXX" } } }, false },
{ "Montenegro", "ME", "", { CallingCodeInfo{ "382", {}, {} } }, false },
{ "Madagascar", "MG", "", { CallingCodeInfo{ "261", {}, { "XX XX XXX XX" } } }, false },
{ "Marshall Islands", "MH", "", { CallingCodeInfo{ "692", {}, {} } }, false },
{ "North Macedonia", "MK", "", { CallingCodeInfo{ "389", {}, { "XX XXX XXX" } } }, false },
{ "Mali", "ML", "", { CallingCodeInfo{ "223", {}, { "XXXX XXXX" } } }, false },
{ "Myanmar", "MM", "", { CallingCodeInfo{ "95", {}, {} } }, false },
{ "Mongolia", "MN", "", { CallingCodeInfo{ "976", {}, { "XX XX XXXX" } } }, false },
{ "Macau", "MO", "", { CallingCodeInfo{ "853", {}, { "XXXX XXXX" } } }, false },
{ "Northern Mariana Islands", "MP", "", { CallingCodeInfo{ "1670", {}, { "XXX XXXX" } } }, false },
{ "Martinique", "MQ", "", { CallingCodeInfo{ "596", {}, {} } }, false },
{ "Mauritania", "MR", "", { CallingCodeInfo{ "222", {}, { "XXXX XXXX" } } }, false },
{ "Montserrat", "MS", "", { CallingCodeInfo{ "1664", {}, { "XXX XXXX" } } }, false },
{ "Malta", "MT", "", { CallingCodeInfo{ "356", {}, { "XX XX XX XX" } } }, false },
{ "Mauritius", "MU", "", { CallingCodeInfo{ "230", {}, { "XXXX XXXX" } } }, false },
{ "Maldives", "MV", "", { CallingCodeInfo{ "960", {}, { "XXX XXXX" } } }, false },
{ "Malawi", "MW", "", { CallingCodeInfo{ "265", {}, { "XX XXX XXXX" } } }, false },
{ "Mexico", "MX", "", { CallingCodeInfo{ "52", {}, {} } }, false },
{ "Malaysia", "MY", "", { CallingCodeInfo{ "60", {}, { "XX XXXX XXXX" } } }, false },
{ "Mozambique", "MZ", "", { CallingCodeInfo{ "258", {}, { "XX XXX XXXX" } } }, false },
{ "Namibia", "NA", "", { CallingCodeInfo{ "264", {}, { "XX XXX XXXX" } } }, false },
{ "New Caledonia", "NC", "", { CallingCodeInfo{ "687", {}, {} } }, false },
{ "Niger", "NE", "", { CallingCodeInfo{ "227", {}, { "XX XX XX XX" } } }, false },
{ "Norfolk Island", "NF", "", { CallingCodeInfo{ "672", {}, {} } }, false },
{ "Nigeria", "NG", "", { CallingCodeInfo{ "234", {}, { "XX XXXX XXXX" } } }, false },
{ "Nicaragua", "NI", "", { CallingCodeInfo{ "505", {}, { "XXXX XXXX" } } }, false },
{ "Netherlands", "NL", "", { CallingCodeInfo{ "31", {}, { "X XX XX XX XX" } } }, false },
{ "Norway", "NO", "", { CallingCodeInfo{ "47", {}, { "XXXX XXXX" } } }, false },
{ "Nepal", "NP", "", { CallingCodeInfo{ "977", {}, { "XX XXXX XXXX" } } }, false },
{ "Nauru", "NR", "", { CallingCodeInfo{ "674", {}, {} } }, false },
{ "Niue", "NU", "", { CallingCodeInfo{ "683", {}, {} } }, false },
{ "New Zealand", "NZ", "", { CallingCodeInfo{ "64", {}, { "XXXX XXXX" } } }, false },
{ "Oman", "OM", "", { CallingCodeInfo{ "968", {}, { "XXXX XXXX" } } }, false },
{ "Panama", "PA", "", { CallingCodeInfo{ "507", {}, { "XXXX XXXX" } } }, false },
{ "Peru", "PE", "", { CallingCodeInfo{ "51", {}, { "XXX XXX XXX" } } }, false },
{ "French Polynesia", "PF", "", { CallingCodeInfo{ "689", {}, {} } }, false },
{ "Papua New Guinea", "PG", "", { CallingCodeInfo{ "675", {}, {} } }, false },
{ "Philippines", "PH", "", { CallingCodeInfo{ "63", {}, { "XXX XXX XXXX" } } }, false },
{ "Pakistan", "PK", "", { CallingCodeInfo{ "92", {}, { "XXX XXX XXXX" } } }, false },
{ "Poland", "PL", "", { CallingCodeInfo{ "48", {}, { "XXX XXX XXX" } } }, false },
{ "Saint Pierre & Miquelon", "PM", "", { CallingCodeInfo{ "508", {}, {} } }, false },
{ "Puerto Rico", "PR", "", { CallingCodeInfo{ "1787", {}, { "XXX XXXX" } } }, false },
{ "Palestine", "PS", "", { CallingCodeInfo{ "970", {}, { "XXX XX XXXX" } } }, false },
{ "Portugal", "PT", "", { CallingCodeInfo{ "351", {}, { "XXX XXX XXX" } } }, false },
{ "Palau", "PW", "", { CallingCodeInfo{ "680", {}, {} } }, false },
{ "Paraguay", "PY", "", { CallingCodeInfo{ "595", {}, { "XXX XXX XXX" } } }, false },
{ "Qatar", "QA", "", { CallingCodeInfo{ "974", {}, { "XX XXX XXX" } } }, false },
{ "Réunion", "RE", "", { CallingCodeInfo{ "262", {}, { "XXX XXX XXX" } } }, false },
{ "Romania", "RO", "", { CallingCodeInfo{ "40", {}, { "XXX XXX XXX" } } }, false },
{ "Serbia", "RS", "", { CallingCodeInfo{ "381", {}, { "XX XXX XXXX" } } }, false },
{ "Russian Federation", "RU", "", { CallingCodeInfo{ "7", {}, { "XXX XXX XXXX" } } }, false },
{ "Rwanda", "RW", "", { CallingCodeInfo{ "250", {}, { "XXX XXX XXX" } } }, false },
{ "Saudi Arabia", "SA", "", { CallingCodeInfo{ "966", {}, { "XX XXX XXXX" } } }, false },
{ "Solomon Islands", "SB", "", { CallingCodeInfo{ "677", {}, {} } }, false },
{ "Seychelles", "SC", "", { CallingCodeInfo{ "248", {}, { "X XX XX XX" } } }, false },
{ "Sudan", "SD", "", { CallingCodeInfo{ "249", {}, { "XX XXX XXXX" } } }, false },
{ "Sweden", "SE", "", { CallingCodeInfo{ "46", {}, { "XX XXX XXXX" } } }, false },
{ "Singapore", "SG", "", { CallingCodeInfo{ "65", {}, { "XXXX XXXX" } } }, false },
{ "Saint Helena", "SH", "", { CallingCodeInfo{ "247", {}, {} } }, false },
{ "Slovenia", "SI", "", { CallingCodeInfo{ "386", {}, { "XX XXX XXX" } } }, false },
{ "Slovakia", "SK", "", { CallingCodeInfo{ "421", {}, { "XXX XXX XXX" } } }, false },
{ "Sierra Leone", "SL", "", { CallingCodeInfo{ "232", {}, { "XX XXX XXX" } } }, false },
{ "San Marino", "SM", "", { CallingCodeInfo{ "378", {}, { "XXX XXX XXXX" } } }, false },
{ "Senegal", "SN", "", { CallingCodeInfo{ "221", {}, { "XX XXX XXXX" } } }, false },
{ "Somalia", "SO", "", { CallingCodeInfo{ "252", {}, { "XX XXX XXX" } } }, false },
{ "Suriname", "SR", "", { CallingCodeInfo{ "597", {}, { "XXX XXXX" } } }, false },
{ "South Sudan", "SS", "", { CallingCodeInfo{ "211", {}, { "XX XXX XXXX" } } }, false },
{ "São Tomé & Príncipe", "ST", "", { CallingCodeInfo{ "239", {}, { "XX XXXXX" } } }, false },
{ "El Salvador", "SV", "", { CallingCodeInfo{ "503", {}, { "XXXX XXXX" } } }, false },
{ "Sint Maarten", "SX", "", { CallingCodeInfo{ "1721", {}, { "XXX XXXX" } } }, false },
{ "Syria", "SY", "", { CallingCodeInfo{ "963", {}, { "XXX XXX XXX" } } }, false },
{ "Eswatini", "SZ", "", { CallingCodeInfo{ "268", {}, { "XXXX XXXX" } } }, false },
{ "Turks & Caicos Islands", "TC", "", { CallingCodeInfo{ "1649", {}, { "XXX XXXX" } } }, false },
{ "Chad", "TD", "", { CallingCodeInfo{ "235", {}, { "XX XX XX XX" } } }, false },
{ "Togo", "TG", "", { CallingCodeInfo{ "228", {}, { "XX XXX XXX" } } }, false },
{ "Thailand", "TH", "", { CallingCodeInfo{ "66", {}, { "X XXXX XXXX" } } }, false },
{ "Tajikistan", "TJ", "", { CallingCodeInfo{ "992", {}, { "XX XXX XXXX" } } }, false },
{ "Tokelau", "TK", "", { CallingCodeInfo{ "690", {}, {} } }, false },
{ "Timor-Leste", "TL", "", { CallingCodeInfo{ "670", {}, {} } }, false },
{ "Turkmenistan", "TM", "", { CallingCodeInfo{ "993", {}, { "XX XXXXXX" } } }, false },
{ "Tunisia", "TN", "", { CallingCodeInfo{ "216", {}, { "XX XXX XXX" } } }, false },
{ "Tonga", "TO", "", { CallingCodeInfo{ "676", {}, {} } }, false },
{ "Turkey", "TR", "", { CallingCodeInfo{ "90", {}, { "XXX XXX XXXX" } } }, false },
{ "Trinidad & Tobago", "TT", "", { CallingCodeInfo{ "1868", {}, { "XXX XXXX" } } }, false },
{ "Tuvalu", "TV", "", { CallingCodeInfo{ "688", {}, {} } }, false },
{ "Taiwan", "TW", "", { CallingCodeInfo{ "886", {}, { "XXX XXX XXX" } } }, false },
{ "Tanzania", "TZ", "", { CallingCodeInfo{ "255", {}, { "XX XXX XXXX" } } }, false },
{ "Ukraine", "UA", "", { CallingCodeInfo{ "380", {}, { "XX XXX XX XX" } } }, false },
{ "Uganda", "UG", "", { CallingCodeInfo{ "256", {}, { "XX XXX XXXX" } } }, false },
{ "USA", "US", "United States of America", { CallingCodeInfo{ "1", {}, { "XXX XXX XXXX" } } }, false },
{ "Uruguay", "UY", "", { CallingCodeInfo{ "598", {}, { "X XXX XXXX" } } }, false },
{ "Uzbekistan", "UZ", "", { CallingCodeInfo{ "998", {}, { "XX XXX XX XX" } } }, false },
{ "Saint Vincent & the Grenadines", "VC", "", { CallingCodeInfo{ "1784", {}, { "XXX XXXX" } } }, false },
{ "Venezuela", "VE", "", { CallingCodeInfo{ "58", {}, { "XXX XXX XXXX" } } }, false },
{ "British Virgin Islands", "VG", "", { CallingCodeInfo{ "1284", {}, { "XXX XXXX" } } }, false },
{ "US Virgin Islands", "VI", "", { CallingCodeInfo{ "1340", {}, { "XXX XXXX" } } }, false },
{ "Vietnam", "VN", "", { CallingCodeInfo{ "84", {}, {} } }, false },
{ "Vanuatu", "VU", "", { CallingCodeInfo{ "678", {}, {} } }, false },
{ "Wallis & Futuna", "WF", "", { CallingCodeInfo{ "681", {}, {} } }, false },
{ "Samoa", "WS", "", { CallingCodeInfo{ "685", {}, {} } }, false },
{ "Kosovo", "XK", "", { CallingCodeInfo{ "383", {}, { "XXXX XXXX" } } }, false },
{ "Yemen", "YE", "", { CallingCodeInfo{ "967", {}, { "XXX XXX XXX" } } }, false },
{ "South Africa", "ZA", "", { CallingCodeInfo{ "27", {}, { "XX XXX XXXX" } } }, false },
{ "Zambia", "ZM", "", { CallingCodeInfo{ "260", {}, { "XX XXX XXXX" } } }, false },
{ "Zimbabwe", "ZW", "", { CallingCodeInfo{ "263", {}, { "XX XXX XXXX" } } }, false },
} };
} // namespace
CountriesInstance::CountriesInstance() {
}
const std::vector<Info> &CountriesInstance::list() {
if (_list.empty()) {
_list = (FallbackList | ranges::to_vector);
}
return _list;
}
void CountriesInstance::setList(std::vector<Info> &&infos) {
_list = std::move(infos);
}
const CountriesInstance::Map &CountriesInstance::byCode() {
if (_byCode.empty()) {
_byCode.reserve(list().size());
for (const auto &entry : list()) {
for (const auto &code : entry.codes) {
_byCode.insert(code.callingCode, &entry);
}
}
}
return _byCode;
}
const CountriesInstance::Map &CountriesInstance::byISO2() {
if (_byISO2.empty()) {
_byISO2.reserve(list().size());
for (const auto &entry : list()) {
_byISO2.insert(entry.iso2, &entry);
}
}
return _byISO2;
}
QString CountriesInstance::validPhoneCode(QString fullCode) {
const auto &listByCode = byCode();
while (fullCode.length()) {
const auto i = listByCode.constFind(fullCode);
if (i != listByCode.cend()) {
return fullCode;
}
fullCode.chop(1);
}
return QString();
}
QString CountriesInstance::countryNameByISO2(const QString &iso) {
const auto &listByISO2 = byISO2();
const auto i = listByISO2.constFind(iso);
return (i != listByISO2.cend()) ? (*i)->name : QString();
}
QString CountriesInstance::countryISO2ByPhone(const QString &phone) {
const auto &listByCode = byCode();
const auto code = validPhoneCode(phone);
const auto i = listByCode.find(code);
return (i != listByCode.cend()) ? (*i)->iso2 : QString();
}
FormatResult CountriesInstance::format(FormatArgs args) {
// Ported from TDLib.
if (args.phone.isEmpty()) {
return FormatResult();
}
const auto &phoneNumber = args.phone;
const Info *bestCountryPtr = nullptr;
const CallingCodeInfo *bestCallingCodePtr = nullptr;
auto bestLength = size_t(0);
[[maybe_unused]] auto isPrefix = false;
for (const auto &country : list()) {
for (auto &callingCode : country.codes) {
if (phoneNumber.startsWith(callingCode.callingCode)) {
const auto codeSize = callingCode.callingCode.size();
for (const auto &prefix : callingCode.prefixes) {
if (prefix.startsWith(phoneNumber.midRef(codeSize))) {
isPrefix = true;
}
if ((codeSize + prefix.size()) > bestLength &&
phoneNumber.midRef(codeSize).startsWith(prefix)) {
bestCountryPtr = &country;
bestCallingCodePtr = &callingCode;
bestLength = codeSize + prefix.size();
}
}
}
if (callingCode.callingCode.startsWith(phoneNumber)) {
isPrefix = true;
}
}
}
if (bestCountryPtr == nullptr) {
return FormatResult{ .formatted = phoneNumber };
}
if (args.onlyCode) {
return FormatResult{ .code = bestCallingCodePtr->callingCode };
}
const auto codeSize = int(bestCallingCodePtr->callingCode.size());
if (args.onlyGroups && args.incomplete) {
auto groups = args.skipCode
? QVector<int>()
: QVector<int>{ codeSize };
auto groupSize = 0;
for (const auto &c : bestCallingCodePtr->patterns.front()) {
if (c == ' ') {
groups.push_back(base::take(groupSize));
} else {
groupSize++;
}
}
if (groupSize) {
groups.push_back(base::take(groupSize));
}
return FormatResult{ .groups = std::move(groups) };
}
const auto formattedPart = phoneNumber.mid(codeSize);
auto formattedResult = formattedPart;
auto groups = QVector<int>();
auto maxMatchedDigits = size_t(0);
for (auto &pattern : bestCallingCodePtr->patterns) {
auto resultGroups = QVector<int>();
auto result = QString();
auto currentPatternPos = int(0);
auto isFailedMatch = false;
auto matchedDigits = size_t(0);
auto groupSize = 0;
for (const auto &c : formattedPart) {
while ((currentPatternPos < pattern.size())
&& (pattern[currentPatternPos] != 'X')
&& !pattern[currentPatternPos].isDigit()) {
if (args.onlyGroups) {
resultGroups.push_back(groupSize);
groupSize = 0;
} else {
result += pattern[currentPatternPos];
}
currentPatternPos++;
}
if (!args.onlyGroups && (currentPatternPos == pattern.size())) {
result += ' ';
}
if ((currentPatternPos >= pattern.size())
|| (pattern[currentPatternPos] == 'X')) {
currentPatternPos++;
if (args.onlyGroups) {
groupSize++;
} else {
result += c;
}
} else {
if (c == pattern[currentPatternPos]) {
matchedDigits++;
currentPatternPos++;
if (args.onlyGroups) {
groupSize++;
} else {
result += c;
}
} else {
isFailedMatch = true;
break;
}
}
}
if (groupSize) {
resultGroups.push_back(groupSize);
}
if (!isFailedMatch && matchedDigits >= maxMatchedDigits) {
maxMatchedDigits = matchedDigits;
if (args.onlyGroups) {
groups = std::move(resultGroups);
} else {
formattedResult = std::move(result);
}
}
}
if (!args.skipCode) {
if (args.onlyGroups) {
groups.push_front(codeSize);
} else {
formattedResult = '+'
+ bestCallingCodePtr->callingCode
+ ' '
+ std::move(formattedResult);
}
}
return FormatResult{
.formatted = (args.onlyGroups
? QString()
: std::move(formattedResult)),
.groups = std::move(groups),
};
}
CountriesInstance &Instance() {
return SingleInstance;
}
QString ExtractPhoneCode(const QString &phone) {
return Instance().format({ .phone = phone, .onlyCode = true }).code;
}
} // namespace Countries

View File

@@ -0,0 +1,70 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Countries {
struct CallingCodeInfo {
QString callingCode;
std::vector<QString> prefixes;
std::vector<QString> patterns;
};
struct Info {
QString name;
QString iso2;
QString alternativeName;
std::vector<CallingCodeInfo> codes;
bool isHidden = false;
};
struct FormatResult {
QString formatted;
QVector<int> groups;
QString code;
};
struct FormatArgs {
QString phone;
bool onlyGroups = false;
bool skipCode = false;
bool incomplete = false;
bool onlyCode = false;
};
class CountriesInstance final {
public:
using Map = QHash<QString, const Info *>;
CountriesInstance();
[[nodiscard]] const std::vector<Info> &list();
void setList(std::vector<Info> &&infos);
[[nodiscard]] const Map &byCode();
[[nodiscard]] const Map &byISO2();
[[nodiscard]] QString validPhoneCode(QString fullCode);
[[nodiscard]] QString countryNameByISO2(const QString &iso);
[[nodiscard]] QString countryISO2ByPhone(const QString &phone);
[[nodiscard]] FormatResult format(FormatArgs args);
private:
std::vector<Info> _list;
Map _byCode;
Map _byISO2;
};
CountriesInstance &Instance();
[[nodiscard]] QString ExtractPhoneCode(const QString &phone);
} // namespace Countries

View File

@@ -0,0 +1,257 @@
/*
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 "countries/countries_manager.h"
#include "core/application.h"
#include "countries/countries_instance.h"
#include "main/main_app_config.h"
#include "main/main_account.h"
#include "main/main_domain.h"
#include "mtproto/mtp_instance.h"
#include <QtCore/QFile>
namespace Countries {
namespace {
struct FileData {
int hash = 0;
std::vector<Info> infos;
};
auto ProcessAlternativeName(Info &&info) {
if (info.name == u"USA"_q) {
info.alternativeName = u"United States of America"_q;
}
return std::move(info);
}
[[nodiscard]] QByteArray SerializeCodeInfo(const CallingCodeInfo &info) {
auto result = QByteArray();
auto stream = QDataStream(&result, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_5_3);
stream
<< info.callingCode
<< int(info.prefixes.size())
<< int(info.patterns.size());
for (const auto &prefix : info.prefixes) {
stream << prefix;
}
for (const auto &pattern : info.patterns) {
stream << pattern;
}
stream.device()->close();
return result;
}
[[nodiscard]] CallingCodeInfo DeserializeCodeInfo(const QByteArray &data) {
auto stream = QDataStream(data);
auto result = CallingCodeInfo();
auto prefixesCount = qint32(0);
auto patternsCount = qint32(0);
stream
>> result.callingCode
>> prefixesCount
>> patternsCount;
for (auto i = 0; i < prefixesCount; i++) {
auto prefix = QString();
stream >> prefix;
result.prefixes.push_back(std::move(prefix));
}
for (auto i = 0; i < patternsCount; i++) {
auto pattern = QString();
stream >> pattern;
result.patterns.push_back(std::move(pattern));
}
return (stream.status() != QDataStream::Ok)
? CallingCodeInfo()
: result;
}
[[nodiscard]] QByteArray SerializeInfo(const Info &info) {
auto result = QByteArray();
auto stream = QDataStream(&result, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_5_3);
stream
<< info.name
<< info.iso2
<< info.alternativeName
<< info.isHidden
<< int(info.codes.size());
for (const auto &code : info.codes) {
stream << SerializeCodeInfo(code);
}
stream.device()->close();
return result;
}
[[nodiscard]] Info DeserializeInfo(const QByteArray &data) {
auto stream = QDataStream(data);
auto result = Info();
auto codesCount = qint32(0);
stream
>> result.name
>> result.iso2
>> result.alternativeName
>> result.isHidden
>> codesCount;
for (auto i = 0; i < codesCount; i++) {
auto code = QByteArray();
stream >> code;
result.codes.push_back(DeserializeCodeInfo(code));
}
return (stream.status() != QDataStream::Ok)
? Info()
: result;
}
[[nodiscard]] QByteArray Serialize(const FileData &data) {
auto result = QByteArray();
auto stream = QDataStream(&result, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_5_3);
stream
<< data.hash
<< int(data.infos.size());
for (const auto &info : data.infos) {
stream << SerializeInfo(info);
}
stream.device()->close();
return result;
}
[[nodiscard]] FileData Deserialize(const QByteArray &data) {
auto stream = QDataStream(data);
auto hash = int(0);
auto infosCount = qint32(0);
auto infos = std::vector<Info>();
stream >> hash >> infosCount;
for (auto i = 0; i < infosCount; i++) {
auto info = QByteArray();
stream >> info;
infos.push_back(DeserializeInfo(info));
}
return (stream.status() != QDataStream::Ok)
? FileData()
: FileData{ .hash = hash, .infos = std::move(infos) };
}
} // namespace
Manager::Manager(not_null<Main::Domain*> domain)
: _path(cWorkingDir() + "tdata/countries") {
read();
domain->activeValue(
) | rpl::map([=](Main::Account *account) {
if (!account) {
_api.reset();
}
return account
? account->mtpMainSessionValue()
: rpl::never<not_null<MTP::Instance*>>();
}) | rpl::flatten_latest(
) | rpl::start_with_next([=](not_null<MTP::Instance*> instance) {
_api.emplace(instance);
request();
}, _lifetime);
}
void Manager::read() {
auto file = QFile(_path);
if (!file.open(QIODevice::ReadOnly)) {
return;
}
auto stream = QDataStream(&file);
auto data = QByteArray();
stream >> data;
auto fileData = Deserialize(data);
_hash = fileData.hash;
Instance().setList(base::take(fileData.infos));
}
void Manager::write() const {
auto file = QFile(_path);
if (!file.open(QIODevice::WriteOnly)) {
return;
}
auto stream = QDataStream(&file);
stream << Serialize({ .hash = _hash, .infos = Instance().list() });
}
void Manager::request() {
Expects(_api.has_value());
const auto convertMTP = [](const auto &vector, bool force = false) {
if (!vector) {
return std::vector<QString>(force ? 1 : 0);
}
return ranges::views::all(
vector->v
) | ranges::views::transform([](const MTPstring &s) -> QString {
return qs(s);
}) | ranges::to_vector;
};
_api->request(MTPhelp_GetCountriesList(
MTP_string(),
MTP_int(_hash)
)).done([=](const MTPhelp_CountriesList &result) {
result.match([&](const MTPDhelp_countriesList &data) {
_hash = data.vhash().v;
auto infos = std::vector<Info>();
for (const auto &country : data.vcountries().v) {
const auto &countryData = country.c_help_country();
if (countryData.is_hidden()) {
continue;
}
auto info = Info(ProcessAlternativeName({
.name = countryData.vdefault_name().v,
.iso2 = countryData.viso2().v,
.isHidden = countryData.is_hidden(),
}));
for (const auto &code : countryData.vcountry_codes().v) {
const auto &codeData = code.c_help_countryCode();
info.codes.push_back(CallingCodeInfo{
.callingCode = codeData.vcountry_code().v,
.prefixes = convertMTP(codeData.vprefixes(), true),
.patterns = convertMTP(codeData.vpatterns()),
});
}
infos.push_back(std::move(info));
}
Instance().setList(std::move(infos));
write();
}, [](const MTPDhelp_countriesListNotModified &data) {
});
_lifetime.destroy();
}).fail([=](const MTP::Error &error) {
LOG(("API Error: getting countries failed with error %1"
).arg(error.type()));
_lifetime.destroy();
}).send();
}
rpl::lifetime &Manager::lifetime() {
return _lifetime;
}
Manager::~Manager() {
}
} // namespace Countries

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
#include "mtproto/sender.h"
namespace Main {
class Domain;
} // namespace Main
namespace Countries {
class Manager final {
public:
Manager(not_null<Main::Domain*> domain);
~Manager();
void read();
void write() const;
rpl::lifetime &lifetime();
private:
void request();
std::optional<MTP::Sender> _api;
const QString _path;
int _hash = 0;
rpl::lifetime _lifetime;
};
} // namespace Countries

View File

@@ -57,7 +57,7 @@ struct PeerUpdate {
Notifications = (1U << 4),
Migration = (1U << 5),
UnavailableReason = (1U << 6),
PinnedMessages = (1U << 7),
ChatThemeEmoji = (1U << 7),
IsBlocked = (1U << 8),
MessagesTTL = (1U << 9),
@@ -118,8 +118,9 @@ struct HistoryUpdate {
BotKeyboard = (1U << 11),
CloudDraft = (1U << 12),
LocalDraftSet = (1U << 13),
PinnedMessages = (1U << 14),
LastUsedBit = (1U << 13),
LastUsedBit = (1U << 14),
};
using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; }
@@ -133,16 +134,17 @@ struct MessageUpdate {
enum class Flag : uint32 {
None = 0,
Edited = (1U << 0),
Destroyed = (1U << 1),
DialogRowRepaint = (1U << 2),
DialogRowRefresh = (1U << 3),
NewAdded = (1U << 4),
ReplyMarkup = (1U << 5),
BotCallbackSent = (1U << 6),
NewMaybeAdded = (1U << 7),
Edited = (1U << 0),
Destroyed = (1U << 1),
DialogRowRepaint = (1U << 2),
DialogRowRefresh = (1U << 3),
NewAdded = (1U << 4),
ReplyMarkup = (1U << 5),
BotCallbackSent = (1U << 6),
NewMaybeAdded = (1U << 7),
RepliesUnreadCount = (1U << 8),
LastUsedBit = (1U << 7),
LastUsedBit = (1U << 7),
};
using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; }

View File

@@ -887,6 +887,7 @@ void ApplyChannelUpdate(
session->changes().peerUpdated(channel, UpdateFlag::StickersSet);
}
}
channel->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
channel->fullUpdated();
if (canViewAdmins != channel->canViewAdmins()

View File

@@ -428,6 +428,7 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
SetTopPinnedMessageId(chat, pinned->v);
}
chat->checkFolder(update.vfolder_id().value_or_empty());
chat->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
chat->fullUpdated();
chat->setAbout(qs(update.vabout()));

View File

@@ -31,21 +31,83 @@ constexpr auto kReloadTimeout = 3600 * crl::time(1000);
CloudTheme CloudTheme::Parse(
not_null<Main::Session*> session,
const MTPDtheme &data) {
const MTPDtheme &data,
bool parseSettings) {
const auto document = data.vdocument();
const auto paper = [&]() -> std::optional<WallPaper> {
if (const auto settings = data.vsettings()) {
return settings->match([&](const MTPDthemeSettings &data) {
return data.vwallpaper()
? WallPaper::Create(session, *data.vwallpaper())
: std::nullopt;
});
}
return {};
};
const auto outgoingMessagesColors = [&] {
auto result = std::vector<QColor>();
if (const auto settings = data.vsettings()) {
settings->match([&](const MTPDthemeSettings &data) {
if (const auto colors = data.vmessage_colors()) {
for (const auto color : colors->v) {
result.push_back(ColorFromSerialized(color));
}
}
});
}
return result;
};
const auto accentColor = [&]() -> std::optional<QColor> {
if (const auto settings = data.vsettings()) {
return settings->match([&](const MTPDthemeSettings &data) {
return ColorFromSerialized(data.vaccent_color().v);
});
}
return {};
};
const auto basedOnDark = [&] {
if (const auto settings = data.vsettings()) {
return settings->match([&](const MTPDthemeSettings &data) {
return data.vbase_theme().match([](
const MTPDbaseThemeNight &) {
return true;
}, [](const MTPDbaseThemeTinted &) {
return true;
}, [](const auto &) {
return false;
});
});
}
return false;
};
return {
data.vid().v,
data.vaccess_hash().v,
qs(data.vslug()),
qs(data.vtitle()),
(document
.id = data.vid().v,
.accessHash = data.vaccess_hash().v,
.slug = qs(data.vslug()),
.title = qs(data.vtitle()),
.documentId = (document
? session->data().processDocument(*document)->id
: DocumentId(0)),
data.is_creator() ? session->userId() : UserId(0),
data.vinstalls_count().v
.createdBy = data.is_creator() ? session->userId() : UserId(0),
.usersCount = data.vinstalls_count().value_or_empty(),
.paper = parseSettings ? paper() : std::nullopt,
.accentColor = parseSettings ? accentColor() : std::nullopt,
.outgoingMessagesColors = (parseSettings
? outgoingMessagesColors()
: std::vector<QColor>()),
.basedOnDark = parseSettings && basedOnDark(),
};
}
CloudTheme CloudTheme::Parse(
not_null<Main::Session*> session,
const MTPTheme &data,
bool parseSettings) {
return data.match([&](const MTPDtheme &data) {
return CloudTheme::Parse(session, data, parseSettings);
});
}
QString CloudThemes::Format() {
static const auto kResult = QString::fromLatin1("tdesktop");
return kResult;
@@ -256,14 +318,14 @@ void CloudThemes::scheduleReload() {
}
void CloudThemes::refresh() {
if (_refreshRquestId) {
if (_refreshRequestId) {
return;
}
_refreshRquestId = _session->api().request(MTPaccount_GetThemes(
_refreshRequestId = _session->api().request(MTPaccount_GetThemes(
MTP_string(Format()),
MTP_int(_hash)
)).done([=](const MTPaccount_Themes &result) {
_refreshRquestId = 0;
_refreshRequestId = 0;
result.match([&](const MTPDaccount_themes &data) {
_hash = data.vhash().v;
parseThemes(data.vthemes().v);
@@ -271,7 +333,7 @@ void CloudThemes::refresh() {
}, [](const MTPDaccount_themesNotModified &) {
});
}).fail([=](const MTP::Error &error) {
_refreshRquestId = 0;
_refreshRequestId = 0;
}).send();
}
@@ -279,13 +341,79 @@ void CloudThemes::parseThemes(const QVector<MTPTheme> &list) {
_list.clear();
_list.reserve(list.size());
for (const auto &theme : list) {
theme.match([&](const MTPDtheme &data) {
_list.push_back(CloudTheme::Parse(_session, data));
});
_list.push_back(CloudTheme::Parse(_session, theme));
}
checkCurrentTheme();
}
void CloudThemes::refreshChatThemes() {
if (_chatThemesRequestId) {
return;
}
_chatThemesRequestId = _session->api().request(MTPaccount_GetChatThemes(
MTP_int(_chatThemesHash)
)).done([=](const MTPaccount_ChatThemes &result) {
_chatThemesRequestId = 0;
result.match([&](const MTPDaccount_chatThemes &data) {
_hash = data.vhash().v;
parseChatThemes(data.vthemes().v);
_chatThemesUpdates.fire({});
}, [](const MTPDaccount_chatThemesNotModified &) {
});
}).fail([=](const MTP::Error &error) {
_chatThemesRequestId = 0;
}).send();
}
const std::vector<ChatTheme> &CloudThemes::chatThemes() const {
return _chatThemes;
}
rpl::producer<> CloudThemes::chatThemesUpdated() const {
return _chatThemesUpdates.events();
}
std::optional<ChatTheme> CloudThemes::themeForEmoji(
const QString &emoji) const {
if (emoji.isEmpty()) {
return {};
}
const auto i = ranges::find(_chatThemes, emoji, &ChatTheme::emoji);
return (i != end(_chatThemes)) ? std::make_optional(*i) : std::nullopt;
}
rpl::producer<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue(
const QString &emoji) {
if (emoji.isEmpty()) {
return rpl::single<std::optional<ChatTheme>>(std::nullopt);
} else if (auto result = themeForEmoji(emoji)) {
return rpl::single(std::move(result));
}
refreshChatThemes();
return rpl::single<std::optional<ChatTheme>>(
std::nullopt
) | rpl::then(chatThemesUpdated(
) | rpl::map([=] {
return themeForEmoji(emoji);
}) | rpl::filter([](const std::optional<ChatTheme> &theme) {
return theme.has_value();
}) | rpl::take(1));
}
void CloudThemes::parseChatThemes(const QVector<MTPChatTheme> &list) {
_chatThemes.clear();
_chatThemes.reserve(list.size());
for (const auto &theme : list) {
theme.match([&](const MTPDchatTheme &data) {
_chatThemes.push_back({
.emoji = qs(data.vemoticon()),
.light = CloudTheme::Parse(_session, data.vtheme(), true),
.dark = CloudTheme::Parse(_session, data.vdark_theme(), true),
});
});
}
}
void CloudThemes::checkCurrentTheme() {
const auto &object = Window::Theme::Background()->themeObject();
if (!object.cloud.id || !object.cloud.documentId) {

View File

@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "base/timer.h"
#include "data/data_wall_paper.h"
class DocumentData;
@@ -32,9 +33,25 @@ struct CloudTheme {
UserId createdBy = 0;
int usersCount = 0;
std::optional<WallPaper> paper;
std::optional<QColor> accentColor;
std::vector<QColor> outgoingMessagesColors;
bool basedOnDark = false;
static CloudTheme Parse(
not_null<Main::Session*> session,
const MTPDtheme &data);
const MTPDtheme &data,
bool parseSettings = false);
static CloudTheme Parse(
not_null<Main::Session*> session,
const MTPTheme &data,
bool parseSettings = false);
};
struct ChatTheme {
QString emoji;
CloudTheme light;
CloudTheme dark;
};
class CloudThemes final {
@@ -49,6 +66,14 @@ public:
void savedFromEditor(const CloudTheme &data);
void remove(uint64 cloudThemeId);
void refreshChatThemes();
[[nodiscard]] const std::vector<ChatTheme> &chatThemes() const;
[[nodiscard]] rpl::producer<> chatThemesUpdated() const;
[[nodiscard]] std::optional<ChatTheme> themeForEmoji(
const QString &emoji) const;
[[nodiscard]] rpl::producer<std::optional<ChatTheme>> themeForEmojiValue(
const QString &emoji);
void applyUpdate(const MTPTheme &theme);
void resolve(
@@ -90,13 +115,20 @@ private:
Fn<void(std::shared_ptr<Data::DocumentMedia>)> callback);
void invokeForLoaded(LoadingDocument &value);
void parseChatThemes(const QVector<MTPChatTheme> &list);
const not_null<Main::Session*> _session;
int32 _hash = 0;
mtpRequestId _refreshRquestId = 0;
mtpRequestId _refreshRequestId = 0;
mtpRequestId _resolveRequestId = 0;
std::vector<CloudTheme> _list;
rpl::event_stream<> _updates;
int32 _chatThemesHash = 0;
mtpRequestId _chatThemesRequestId = 0;
std::vector<ChatTheme> _chatThemes;
rpl::event_stream<> _chatThemesUpdates;
base::Timer _reloadCurrentTimer;
LoadingDocument _updatingFrom;
LoadingDocument _previewFrom;

View File

@@ -1,301 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_countries.h"
namespace Data {
namespace {
const std::array<CountryInfo, 231> List = { {
{ "Afghanistan", "AF", "93" },
{ "Albania", "AL", "355" },
{ "Algeria", "DZ", "213" },
{ "American Samoa", "AS", "1684" },
{ "Andorra", "AD", "376" },
{ "Angola", "AO", "244" },
{ "Anguilla", "AI", "1264" },
{ "Antigua & Barbuda", "AG", "1268" },
{ "Argentina", "AR", "54" },
{ "Armenia", "AM", "374" },
{ "Aruba", "AW", "297" },
{ "Australia", "AU", "61" },
{ "Austria", "AT", "43" },
{ "Azerbaijan", "AZ", "994" },
{ "Bahamas", "BS", "1242" },
{ "Bahrain", "BH", "973" },
{ "Bangladesh", "BD", "880" },
{ "Barbados", "BB", "1246" },
{ "Belarus", "BY", "375" },
{ "Belgium", "BE", "32" },
{ "Belize", "BZ", "501" },
{ "Benin", "BJ", "229" },
{ "Bermuda", "BM", "1441" },
{ "Bhutan", "BT", "975" },
{ "Bolivia", "BO", "591" },
{ "Bonaire, Sint Eustatius & Saba", "BQ", "599" },
{ "Bosnia & Herzegovina", "BA", "387" },
{ "Botswana", "BW", "267" },
{ "Brazil", "BR", "55" },
{ "British Virgin Islands", "VG", "1284" },
{ "Brunei Darussalam", "BN", "673" },
{ "Bulgaria", "BG", "359" },
{ "Burkina Faso", "BF", "226" },
{ "Burundi", "BI", "257" },
{ "Cambodia", "KH", "855" },
{ "Cameroon", "CM", "237" },
{ "Canada", "CA", "1" },
{ "Cape Verde", "CV", "238" },
{ "Cayman Islands", "KY", "1345" },
{ "Central African Republic", "CF", "236" },
{ "Chad", "TD", "235" },
{ "Chile", "CL", "56" },
{ "China", "CN", "86" },
{ "Colombia", "CO", "57" },
{ "Comoros", "KM", "269" },
{ "Congo", "CG", "242" },
{ "Congo, Democratic Republic", "CD", "243" },
{ "Cook Islands", "CK", "682" },
{ "Costa Rica", "CR", "506" },
{ "Croatia", "HR", "385" },
{ "Cuba", "CU", "53" },
{ "Curaçao", "CW", "599" },
{ "Cyprus", "CY", "357" },
{ "Czech Republic", "CZ", "420" },
{ "Côte d`Ivoire", "CI", "225" },
{ "Denmark", "DK", "45" },
{ "Diego Garcia", "IO", "246" },
{ "Djibouti", "DJ", "253" },
{ "Dominica", "DM", "1767" },
{ "Dominican Republic", "DO", "1" },
{ "Ecuador", "EC", "593" },
{ "Egypt", "EG", "20" },
{ "El Salvador", "SV", "503" },
{ "Equatorial Guinea", "GQ", "240" },
{ "Eritrea", "ER", "291" },
{ "Estonia", "EE", "372" },
{ "Ethiopia", "ET", "251" },
{ "Falkland Islands", "FK", "500" },
{ "Faroe Islands", "FO", "298" },
{ "Fiji", "FJ", "679" },
{ "Finland", "FI", "358" },
{ "France", "FR", "33" },
{ "French Guiana", "GF", "594" },
{ "French Polynesia", "PF", "689" },
{ "Gabon", "GA", "241" },
{ "Gambia", "GM", "220" },
{ "Georgia", "GE", "995" },
{ "Germany", "DE", "49" },
{ "Ghana", "GH", "233" },
{ "Gibraltar", "GI", "350" },
{ "Greece", "GR", "30" },
{ "Greenland", "GL", "299" },
{ "Grenada", "GD", "1473" },
{ "Guadeloupe", "GP", "590" },
{ "Guam", "GU", "1671" },
{ "Guatemala", "GT", "502" },
{ "Guinea", "GN", "224" },
{ "Guinea-Bissau", "GW", "245" },
{ "Guyana", "GY", "592" },
{ "Haiti", "HT", "509" },
{ "Honduras", "HN", "504" },
{ "Hong Kong", "HK", "852" },
{ "Hungary", "HU", "36" },
{ "Iceland", "IS", "354" },
{ "India", "IN", "91" },
{ "Indonesia", "ID", "62" },
{ "Iran", "IR", "98" },
{ "Iraq", "IQ", "964" },
{ "Ireland", "IE", "353" },
{ "Israel", "IL", "972" },
{ "Italy", "IT", "39" },
{ "Jamaica", "JM", "1876" },
{ "Japan", "JP", "81" },
{ "Jordan", "JO", "962" },
{ "Kazakhstan", "KZ", "7" },
{ "Kenya", "KE", "254" },
{ "Kiribati", "KI", "686" },
{ "Kuwait", "KW", "965" },
{ "Kyrgyzstan", "KG", "996" },
{ "Laos", "LA", "856" },
{ "Latvia", "LV", "371" },
{ "Lebanon", "LB", "961" },
{ "Lesotho", "LS", "266" },
{ "Liberia", "LR", "231" },
{ "Libya", "LY", "218" },
{ "Liechtenstein", "LI", "423" },
{ "Lithuania", "LT", "370" },
{ "Luxembourg", "LU", "352" },
{ "Macau", "MO", "853" },
{ "Macedonia", "MK", "389" },
{ "Madagascar", "MG", "261" },
{ "Malawi", "MW", "265" },
{ "Malaysia", "MY", "60" },
{ "Maldives", "MV", "960" },
{ "Mali", "ML", "223" },
{ "Malta", "MT", "356" },
{ "Marshall Islands", "MH", "692" },
{ "Martinique", "MQ", "596" },
{ "Mauritania", "MR", "222" },
{ "Mauritius", "MU", "230" },
{ "Mexico", "MX", "52" },
{ "Micronesia", "FM", "691" },
{ "Moldova", "MD", "373" },
{ "Monaco", "MC", "377" },
{ "Mongolia", "MN", "976" },
{ "Montenegro", "ME", "382" },
{ "Montserrat", "MS", "1664" },
{ "Morocco", "MA", "212" },
{ "Mozambique", "MZ", "258" },
{ "Myanmar", "MM", "95" },
{ "Namibia", "NA", "264" },
{ "Nauru", "NR", "674" },
{ "Nepal", "NP", "977" },
{ "Netherlands", "NL", "31" },
{ "New Caledonia", "NC", "687" },
{ "New Zealand", "NZ", "64" },
{ "Nicaragua", "NI", "505" },
{ "Niger", "NE", "227" },
{ "Nigeria", "NG", "234" },
{ "Niue", "NU", "683" },
{ "Norfolk Island", "NF", "672" },
{ "North Korea", "KP", "850" },
{ "Northern Mariana Islands", "MP", "1670" },
{ "Norway", "NO", "47" },
{ "Oman", "OM", "968" },
{ "Pakistan", "PK", "92" },
{ "Palau", "PW", "680" },
{ "Palestine", "PS", "970" },
{ "Panama", "PA", "507" },
{ "Papua New Guinea", "PG", "675" },
{ "Paraguay", "PY", "595" },
{ "Peru", "PE", "51" },
{ "Philippines", "PH", "63" },
{ "Poland", "PL", "48" },
{ "Portugal", "PT", "351" },
{ "Puerto Rico", "PR", "1" },
{ "Qatar", "QA", "974" },
{ "Romania", "RO", "40" },
{ "Russian Federation", "RU", "7" },
{ "Rwanda", "RW", "250" },
{ "Réunion", "RE", "262" },
{ "Saint Helena", "SH", "247" },
{ "Saint Helena", "SH2", "290" },
{ "Saint Kitts & Nevis", "KN", "1869" },
{ "Saint Lucia", "LC", "1758" },
{ "Saint Pierre & Miquelon", "PM", "508" },
{ "Saint Vincent & the Grenadines", "VC", "1784" },
{ "Samoa", "WS", "685" },
{ "San Marino", "SM", "378" },
{ "Saudi Arabia", "SA", "966" },
{ "Senegal", "SN", "221" },
{ "Serbia", "RS", "381" },
{ "Seychelles", "SC", "248" },
{ "Sierra Leone", "SL", "232" },
{ "Singapore", "SG", "65" },
{ "Sint Maarten", "SX", "1721" },
{ "Slovakia", "SK", "421" },
{ "Slovenia", "SI", "386" },
{ "Solomon Islands", "SB", "677" },
{ "Somalia", "SO", "252" },
{ "South Africa", "ZA", "27" },
{ "South Korea", "KR", "82" },
{ "South Sudan", "SS", "211" },
{ "Spain", "ES", "34" },
{ "Sri Lanka", "LK", "94" },
{ "Sudan", "SD", "249" },
{ "Suriname", "SR", "597" },
{ "Swaziland", "SZ", "268" },
{ "Sweden", "SE", "46" },
{ "Switzerland", "CH", "41" },
{ "Syrian Arab Republic", "SY", "963" },
{ "São Tomé & Príncipe", "ST", "239" },
{ "Taiwan", "TW", "886" },
{ "Tajikistan", "TJ", "992" },
{ "Tanzania", "TZ", "255" },
{ "Thailand", "TH", "66" },
{ "Timor-Leste", "TL", "670" },
{ "Togo", "TG", "228" },
{ "Tokelau", "TK", "690" },
{ "Tonga", "TO", "676" },
{ "Trinidad & Tobago", "TT", "1868" },
{ "Tunisia", "TN", "216" },
{ "Turkey", "TR", "90" },
{ "Turkmenistan", "TM", "993" },
{ "Turks & Caicos Islands", "TC", "1649" },
{ "Tuvalu", "TV", "688" },
{ "US Virgin Islands", "VI", "1340" },
{ "USA", "US", "1", "United States of America" },
{ "Uganda", "UG", "256" },
{ "Ukraine", "UA", "380" },
{ "United Arab Emirates", "AE", "971" },
{ "United Kingdom", "GB", "44" },
{ "Uruguay", "UY", "598" },
{ "Uzbekistan", "UZ", "998" },
{ "Vanuatu", "VU", "678" },
{ "Venezuela", "VE", "58" },
{ "Vietnam", "VN", "84" },
{ "Wallis & Futuna", "WF", "681" },
{ "Yemen", "YE", "967" },
{ "Zambia", "ZM", "260" },
{ "Zimbabwe", "ZW", "263" },
} };
QHash<QString, const CountryInfo *> ByCode;
QHash<QString, const CountryInfo *> ByISO2;
} // namespace
const std::array<CountryInfo, 231> &Countries() {
return List;
}
const QHash<QString, const CountryInfo *> &CountriesByCode() {
if (ByCode.isEmpty()) {
ByCode.reserve(List.size());
for (const auto &entry : List) {
ByCode.insert(entry.code, &entry);
}
}
return ByCode;
}
const QHash<QString, const CountryInfo *> &CountriesByISO2() {
if (ByISO2.isEmpty()) {
ByISO2.reserve(List.size());
for (const auto &entry : List) {
ByISO2.insert(entry.iso2, &entry);
}
}
return ByISO2;
}
QString ValidPhoneCode(QString fullCode) {
const auto &byCode = CountriesByCode();
while (fullCode.length()) {
const auto i = byCode.constFind(fullCode);
if (i != byCode.cend()) {
return (*i)->code;
}
fullCode.chop(1);
}
return QString();
}
QString CountryNameByISO2(const QString &iso) {
const auto &byISO2 = CountriesByISO2();
const auto i = byISO2.constFind(iso);
return (i != byISO2.cend()) ? QString::fromUtf8((*i)->name) : QString();
}
QString CountryISO2ByPhone(const QString &phone) {
const auto &byCode = Data::CountriesByCode();
const auto code = Data::ValidPhoneCode(phone);
const auto i = byCode.find(code);
return (i != byCode.cend()) ? QString::fromUtf8((*i)->iso2) : QString();
}
} // namespace Data

View File

@@ -1,29 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Data {
struct CountryInfo {
const char *name = nullptr;
const char *iso2 = nullptr;
const char *code = nullptr;
const char *alternativeName = nullptr;
};
[[nodiscard]] const std::array<CountryInfo, 231> &Countries();
[[nodiscard]] const QHash<QString, const CountryInfo *> &CountriesByCode();
[[nodiscard]] const QHash<QString, const CountryInfo *> &CountriesByISO2();
[[nodiscard]] QString ValidPhoneCode(QString fullCode);
[[nodiscard]] QString CountryNameByISO2(const QString &iso);
[[nodiscard]] QString CountryISO2ByPhone(const QString &phone);
} // namespace Data

View File

@@ -45,7 +45,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "core/application.h"
#include "lottie/lottie_animation.h"
#include "app.h"
#include <QtCore/QBuffer>
#include <QtCore/QMimeType>
@@ -412,7 +411,7 @@ void DocumentData::setattributes(
void DocumentData::validateLottieSticker() {
if (type == FileDocument
&& _mimeString == qstr("application/x-tgsticker")) {
&& hasMimeType(qstr("application/x-tgsticker"))) {
type = StickerDocument;
_additional = std::make_unique<StickerData>();
sticker()->animated = true;
@@ -443,9 +442,8 @@ bool DocumentData::checkWallPaperProperties() {
|| !dimensions.height()
|| dimensions.width() > Storage::kMaxWallPaperDimension
|| dimensions.height() > Storage::kMaxWallPaperDimension
|| size > Storage::kMaxWallPaperInMemory
|| mimeString() == qstr("application/x-tgwallpattern")) {
return false; // #TODO themes support svg patterns
|| size > Storage::kMaxWallPaperInMemory) {
return false;
}
type = WallPaperDocument;
return true;
@@ -488,9 +486,18 @@ bool DocumentData::isWallPaper() const {
}
bool DocumentData::isPatternWallPaper() const {
return isWallPaper()
&& (isPatternWallPaperPNG() || isPatternWallPaperSVG());
}
bool DocumentData::isPatternWallPaperPNG() const {
return isWallPaper() && hasMimeType(qstr("image/png"));
}
bool DocumentData::isPatternWallPaperSVG() const {
return isWallPaper() && hasMimeType(qstr("application/x-tgwallpattern"));
}
bool DocumentData::hasThumbnail() const {
return _thumbnail.location.valid();
}
@@ -662,9 +669,9 @@ bool DocumentData::saveToCache() const {
&& ((type == StickerDocument)
|| isAnimation()
|| isVoiceMessage()
|| (type == WallPaperDocument)
|| isWallPaper()
|| isTheme()
|| (mimeString() == qstr("image/png")
|| (hasMimeType(qstr("image/png"))
&& _filename.startsWith("image_")));
}
@@ -1234,11 +1241,12 @@ QString DocumentData::mimeString() const {
}
bool DocumentData::hasMimeType(QLatin1String mime) const {
return !_mimeString.compare(mime, Qt::CaseInsensitive);
return (_mimeString == mime);
}
void DocumentData::setMimeString(const QString &mime) {
_mimeString = mime;
_mimeString = std::move(_mimeString).toLower();
}
MediaKey DocumentData::mediaKey() const {
@@ -1264,7 +1272,7 @@ uint8 DocumentData::cacheTag() const {
return Data::kVideoMessageCacheTag;
} else if (isAnimation()) {
return Data::kAnimationCacheTag;
} else if (type == WallPaperDocument) {
} else if (isWallPaper()) {
return Data::kImageCacheTag;
}
return 0;
@@ -1299,14 +1307,9 @@ bool DocumentData::isGifv() const {
}
bool DocumentData::isTheme() const {
return
_mimeString == qstr("application/x-tgtheme-tdesktop")
|| _filename.endsWith(
qstr(".tdesktop-theme"),
Qt::CaseInsensitive)
|| _filename.endsWith(
qstr(".tdesktop-palette"),
Qt::CaseInsensitive);
return hasMimeType(qstr("application/x-tgtheme-tdesktop"))
|| _filename.endsWith(qstr(".tdesktop-theme"), Qt::CaseInsensitive)
|| _filename.endsWith(qstr(".tdesktop-palette"), Qt::CaseInsensitive);
}
bool DocumentData::isSong() const {

View File

@@ -155,6 +155,8 @@ public:
bool checkWallPaperProperties();
[[nodiscard]] bool isWallPaper() const;
[[nodiscard]] bool isPatternWallPaper() const;
[[nodiscard]] bool isPatternWallPaperPNG() const;
[[nodiscard]] bool isPatternWallPaperSVG() const;
[[nodiscard]] bool hasThumbnail() const;
[[nodiscard]] bool thumbnailLoading() const;

View File

@@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "storage/file_download.h"
#include "ui/image/image.h"
#include "app.h"
#include <QtCore/QBuffer>
#include <QtGui/QImageReader>
@@ -40,6 +39,8 @@ enum class FileType {
Video,
AnimatedSticker,
WallPaper,
WallPatternPNG,
WallPatternSVG,
Theme,
};
@@ -61,6 +62,15 @@ enum class FileType {
return Lottie::ReadThumbnail(Lottie::ReadContent(data, path));
} else if (type == FileType::Theme) {
return Window::Theme::GeneratePreview(data, path);
} else if (type == FileType::WallPatternSVG) {
return Images::Read({
.path = path,
.content = std::move(data),
.maxSize = QSize(
kWallPaperThumbnailLimit,
kWallPaperThumbnailLimit),
.gzipSvg = true,
}).image;
}
auto buffer = QBuffer(&data);
auto file = QFile(path);
@@ -391,7 +401,11 @@ void DocumentMedia::checkStickerLarge(not_null<FileLoader*> loader) {
void DocumentMedia::GenerateGoodThumbnail(
not_null<DocumentData*> document,
QByteArray data) {
const auto type = document->isWallPaper()
const auto type = document->isPatternWallPaperSVG()
? FileType::WallPatternSVG
: document->isPatternWallPaperPNG()
? FileType::WallPatternPNG
: document->isWallPaper()
? FileType::WallPaper
: document->isTheme()
? FileType::Theme
@@ -416,7 +430,8 @@ void DocumentMedia::GenerateGoodThumbnail(
auto buffer = QBuffer(&bytes);
const auto format = (type == FileType::AnimatedSticker)
? "WEBP"
: (type == FileType::WallPaper && result.hasAlphaChannel())
: (type == FileType::WallPatternPNG
|| type == FileType::WallPatternSVG)
? "PNG"
: "JPG";
result.save(&buffer, format, kGoodThumbQuality);
@@ -464,11 +479,11 @@ void DocumentMedia::ReadOrGenerateThumbnail(
});
} else if (active) {
crl::async([=] {
const auto image = App::readImage(value, nullptr, false);
crl::on_main(guard, [=] {
auto image = Images::Read({ .content = value }).image;
crl::on_main(guard, [=, image = std::move(image)]() mutable {
document->setGoodThumbnailChecked(true);
if (const auto active = document->activeMediaView()) {
active->setGoodThumbnail(image);
active->setGoodThumbnail(std::move(image));
}
});
});

View File

@@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_document_resolver.h"
#include "app.h"
#include "facades.h"
#include "base/platform/base_platform_info.h"
#include "boxes/confirm_box.h"
@@ -24,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h"
#include "media/player/media_player_instance.h"
#include "platform/platform_file_utilities.h"
#include "ui/chat/chat_theme.h"
#include "ui/text/text_utilities.h"
#include "window/window_session_controller.h"
@@ -174,29 +174,21 @@ bool IsIpRevealingName(const QString &filepath) {
);
}
base::binary_guard ReadImageAsync(
base::binary_guard ReadBackgroundImageAsync(
not_null<Data::DocumentMedia*> media,
FnMut<QImage(QImage)> postprocess,
FnMut<void(QImage&&)> done) {
auto result = base::binary_guard();
const auto gzipSvg = media->owner()->isPatternWallPaperSVG();
crl::async([
gzipSvg,
bytes = media->bytes(),
path = media->owner()->filepath(),
postprocess = std::move(postprocess),
guard = result.make_guard(),
callback = std::move(done)
]() mutable {
auto format = QByteArray();
if (bytes.isEmpty()) {
QFile f(path);
if (f.size() <= App::kImageSizeLimit
&& f.open(QIODevice::ReadOnly)) {
bytes = f.readAll();
}
}
auto image = bytes.isEmpty()
? QImage()
: App::readImage(bytes, &format, false, nullptr);
auto image = Ui::ReadBackgroundImage(path, bytes, gzipSvg);
if (postprocess) {
image = postprocess(std::move(image));
}
@@ -231,7 +223,7 @@ void ResolveDocument(
const auto media = document->createMediaView();
const auto openImageInApp = [&] {
if (document->size >= App::kImageSizeLimit) {
if (document->size >= Images::kReadBytesLimit) {
return false;
}
const auto &location = document->location(true);

View File

@@ -24,7 +24,7 @@ class DocumentMedia;
// [[nodiscard]] bool IsValidMediaFile(const QString &filepath);
[[nodiscard]] bool IsExecutableName(const QString &filepath);
[[nodiscard]] bool IsIpRevealingName(const QString &filepath);
base::binary_guard ReadImageAsync(
base::binary_guard ReadBackgroundImageAsync(
not_null<Data::DocumentMedia*> media,
FnMut<QImage(QImage)> postprocess,
FnMut<void(QImage&&)> done);

View File

@@ -496,8 +496,9 @@ void GroupCall::reload() {
}
_reloadByQueuedUpdatesTimer.cancel();
const auto limit = 3;
_reloadRequestId = api().request(
MTPphone_GetGroupCall(input())
MTPphone_GetGroupCall(input(), MTP_int(limit))
).done([=](const MTPphone_GroupCall &result) {
if (requestParticipantsAfterReload(result)) {
_savedFull = result;

View File

@@ -244,6 +244,14 @@ bool Media::forwardedBecomesUnread() const {
return false;
}
bool Media::dropForwardedInfo() const {
return false;
}
bool Media::forceForwardedInfo() const {
return false;
}
QString Media::errorTextForForward(not_null<PeerData*> peer) const {
return QString();
}
@@ -625,6 +633,10 @@ bool MediaFile::forwardedBecomesUnread() const {
|| _document->isVideoMessage();
}
bool MediaFile::dropForwardedInfo() const {
return _document->isSong();
}
QString MediaFile::errorTextForForward(not_null<PeerData*> peer) const {
if (const auto sticker = _document->sticker()) {
if (const auto error = Data::RestrictionError(
@@ -1007,7 +1019,8 @@ WebPageData *MediaWebPage::webpage() const {
bool MediaWebPage::hasReplyPreview() const {
if (const auto document = MediaWebPage::document()) {
return document->hasThumbnail() && !document->isPatternWallPaper();
return document->hasThumbnail()
&& !document->isPatternWallPaper();
} else if (const auto photo = MediaWebPage::photo()) {
return !photo->isNull();
}
@@ -1139,6 +1152,10 @@ QString MediaGame::errorTextForForward(not_null<PeerData*> peer) const {
).value_or(QString());
}
bool MediaGame::dropForwardedInfo() const {
return true;
}
bool MediaGame::consumeMessageText(const TextWithEntities &text) {
_consumedText = text;
return true;
@@ -1346,6 +1363,10 @@ TextForMimeData MediaDice::clipboardText() const {
return { notificationText() };
}
bool MediaDice::forceForwardedInfo() const {
return true;
}
bool MediaDice::updateInlineResultMedia(const MTPMessageMedia &media) {
return updateSentMedia(media);
}

View File

@@ -102,6 +102,8 @@ public:
virtual bool allowsEditMedia() const;
virtual bool allowsRevoke(TimeId now) const;
virtual bool forwardedBecomesUnread() const;
virtual bool dropForwardedInfo() const;
virtual bool forceForwardedInfo() const;
virtual QString errorTextForForward(not_null<PeerData*> peer) const;
[[nodiscard]] virtual bool consumeMessageText(
@@ -191,6 +193,7 @@ public:
bool allowsEditCaption() const override;
bool allowsEditMedia() const override;
bool forwardedBecomesUnread() const override;
bool dropForwardedInfo() const override;
QString errorTextForForward(not_null<PeerData*> peer) const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
@@ -352,6 +355,7 @@ public:
QString pinnedTextSubstring() const override;
TextForMimeData clipboardText() const override;
QString errorTextForForward(not_null<PeerData*> peer) const override;
bool dropForwardedInfo() const override;
bool consumeMessageText(const TextWithEntities &text) override;
TextWithEntities consumedMessageText() const override;
@@ -442,6 +446,8 @@ public:
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextForMimeData clipboardText() const override;
bool forceForwardedInfo() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
std::unique_ptr<HistoryView::Media> createView(

View File

@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "data/data_histories.h"
#include "data/data_cloud_themes.h"
#include "base/unixtime.h"
#include "base/crc32hash.h"
#include "lang/lang_keys.h"
@@ -527,15 +528,6 @@ bool PeerData::canEditMessagesIndefinitely() const {
Unexpected("Peer type in PeerData::canEditMessagesIndefinitely.");
}
bool PeerData::hasPinnedMessages() const {
return _hasPinnedMessages;
}
void PeerData::setHasPinnedMessages(bool has) {
_hasPinnedMessages = has;
session().changes().peerUpdated(this, UpdateFlag::PinnedMessages);
}
bool PeerData::canExportChatHistory() const {
if (isRepliesChat()) {
return false;
@@ -1012,6 +1004,21 @@ PeerId PeerData::groupCallDefaultJoinAs() const {
return 0;
}
void PeerData::setThemeEmoji(const QString &emoji) {
if (_themeEmoji == emoji) {
return;
}
_themeEmoji = emoji;
if (!emoji.isEmpty() && !owner().cloudThemes().themeForEmoji(emoji)) {
owner().cloudThemes().refreshChatThemes();
}
session().changes().peerUpdated(this, UpdateFlag::ChatThemeEmoji);
}
const QString &PeerData::themeEmoji() const {
return _themeEmoji;
}
void PeerData::setIsBlocked(bool is) {
const auto status = is
? BlockStatus::Blocked
@@ -1154,7 +1161,7 @@ void SetTopPinnedMessageId(not_null<PeerData*> peer, MsgId messageId) {
Storage::SharedMediaType::Pinned,
messageId,
{ messageId, ServerMaxMsgId }));
peer->setHasPinnedMessages(true);
peer->owner().history(peer)->setHasPinnedMessages(true);
}
FullMsgId ResolveTopPinnedId(

View File

@@ -406,9 +406,6 @@ public:
[[nodiscard]] bool canPinMessages() const;
[[nodiscard]] bool canEditMessagesIndefinitely() const;
[[nodiscard]] bool hasPinnedMessages() const;
void setHasPinnedMessages(bool has);
[[nodiscard]] bool canExportChatHistory() const;
// Returns true if about text was changed.
@@ -470,6 +467,9 @@ public:
[[nodiscard]] Data::GroupCall *groupCall() const;
[[nodiscard]] PeerId groupCallDefaultJoinAs() const;
void setThemeEmoji(const QString &emoji);
[[nodiscard]] const QString &themeEmoji() const;
const PeerId id;
QString name;
MTPinputPeer input = MTP_inputPeerEmpty();
@@ -508,13 +508,13 @@ private:
crl::time _lastFullUpdate = 0;
TimeId _ttlPeriod = 0;
bool _hasPinnedMessages = false;
Settings _settings = PeerSettings(PeerSetting::Unknown);
BlockStatus _blockStatus = BlockStatus::Unknown;
LoadedStatus _loadedStatus = LoadedStatus::Not;
QString _about;
QString _themeEmoji;
};

View File

@@ -19,7 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/file_download.h"
#include "core/application.h"
#include "facades.h"
#include "app.h"
namespace {

View File

@@ -101,6 +101,15 @@ rpl::producer<MessagesSlice> RepliesList::source(
_partLoaded.events(
) | rpl::start_with_next(pushDelayed, lifetime);
_history->session().data().channelDifferenceTooLong(
) | rpl::filter([=](not_null<ChannelData*> channel) {
if (_history->peer != channel || !_skippedAfter.has_value()) {
return false;
}
_skippedAfter = std::nullopt;
return true;
}) | rpl::start_with_next(pushDelayed, lifetime);
push();
return lifetime;
};
@@ -169,6 +178,64 @@ rpl::producer<int> RepliesList::fullCount() const {
return _fullCount.value() | rpl::filter_optional();
}
std::optional<int> RepliesList::fullUnreadCountAfter(
MsgId readTillId,
MsgId wasReadTillId,
std::optional<int> wasUnreadCountAfter) const {
Expects(readTillId >= wasReadTillId);
readTillId = std::max(readTillId, _rootId);
wasReadTillId = std::max(wasReadTillId, _rootId);
const auto backLoaded = (_skippedBefore == 0);
const auto frontLoaded = (_skippedAfter == 0);
const auto fullLoaded = backLoaded && frontLoaded;
const auto allUnread = (readTillId == _rootId)
|| (fullLoaded && _list.empty());
const auto countIncoming = [&](auto from, auto till) {
auto &owner = _history->owner();
const auto channelId = _history->channelId();
auto count = 0;
for (auto i = from; i != till; ++i) {
if (!owner.message(channelId, *i)->out()) {
++count;
}
}
return count;
};
if (allUnread && fullLoaded) {
// Should not happen too often unless the list is empty.
return countIncoming(begin(_list), end(_list));
} else if (frontLoaded && !_list.empty() && readTillId >= _list.front()) {
// Always "count by local data" if read till the end.
return 0;
} else if (wasReadTillId == readTillId) {
// Otherwise don't recount the same value over and over.
return wasUnreadCountAfter;
} else if (frontLoaded && !_list.empty() && readTillId >= _list.back()) {
// And count by local data if it is available and read-till changed.
return countIncoming(
begin(_list),
ranges::lower_bound(_list, readTillId, std::greater<>()));
} else if (_list.empty()) {
return std::nullopt;
} else if (wasUnreadCountAfter.has_value()
&& (frontLoaded || readTillId <= _list.front())
&& (backLoaded || wasReadTillId >= _list.back())) {
// Count how many were read since previous value.
const auto from = ranges::lower_bound(
_list,
readTillId,
std::greater<>());
const auto till = ranges::lower_bound(
from,
end(_list),
wasReadTillId,
std::greater<>());
return std::max(*wasUnreadCountAfter - countIncoming(from, till), 0);
}
return std::nullopt;
}
void RepliesList::injectRootMessageAndReverse(not_null<Viewer*> viewer) {
injectRootMessage(viewer);
ranges::reverse(viewer->slice.ids);

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