diff --git a/.github/workflows/no-response.yml b/.github/workflows/cant-reproduce.yml similarity index 57% rename from .github/workflows/no-response.yml rename to .github/workflows/cant-reproduce.yml index f414a55fd..5c4edcba0 100644 --- a/.github/workflows/no-response.yml +++ b/.github/workflows/cant-reproduce.yml @@ -1,32 +1,11 @@ -name: No Response +name: Can't reproduce. -# Both `issue_comment` and `scheduled` event types are required for this Action -# to work properly. on: - issue_comment: - types: [created] schedule: - - cron: '0 0 * * *' + - cron: '0 3 * * *' jobs: - waiting-for-answer: - runs-on: ubuntu-latest - steps: - - uses: lee-dohm/no-response@v0.5.0 - with: - token: ${{ github.token }} - responseRequiredLabel: waiting for answer - - needs-user-action: - runs-on: ubuntu-latest - steps: - - uses: lee-dohm/no-response@v0.5.0 - with: - token: ${{ github.token }} - responseRequiredLabel: needs user action - cant-reproduce: - if: github.event_name != 'issue_comment' runs-on: ubuntu-latest steps: - uses: lee-dohm/no-response@v0.5.0 diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 4eaf2ec22..8020e46c5 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -49,7 +49,6 @@ jobs: defines: - "" - "DESKTOP_APP_DISABLE_X11_INTEGRATION" - - "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION" env: UPLOAD_ARTIFACT: "true" diff --git a/.github/workflows/mac_packaged.yml b/.github/workflows/mac_packaged.yml index 8ed4ffca1..d04b284de 100644 --- a/.github/workflows/mac_packaged.yml +++ b/.github/workflows/mac_packaged.yml @@ -49,7 +49,7 @@ jobs: env: GIT: "https://github.com" - OPENALDIR: "/usr/local/opt/openal-soft" + CMAKE_PREFIX_PATH: "/usr/local/opt/ffmpeg@6:/usr/local/opt/openal-soft" UPLOAD_ARTIFACT: "true" ONLY_CACHE: "false" MANUAL_CACHING: "1" @@ -69,7 +69,7 @@ jobs: run: | brew update brew upgrade || true - brew install autoconf automake boost cmake ffmpeg openal-soft openssl opus ninja pkg-config python qt yasm xz + brew install autoconf automake boost cmake ffmpeg@6 openal-soft openssl opus ninja pkg-config python qt yasm xz sudo xcode-select -s /Applications/Xcode.app/Contents/Developer xcodebuild -version > CACHE_KEY.txt diff --git a/.github/workflows/needs-user-action.yml b/.github/workflows/needs-user-action.yml new file mode 100644 index 000000000..46ad9f87d --- /dev/null +++ b/.github/workflows/needs-user-action.yml @@ -0,0 +1,16 @@ +name: Needs user action. + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 2 * * *' + +jobs: + needs-user-action: + runs-on: ubuntu-latest + steps: + - uses: lee-dohm/no-response@v0.5.0 + with: + token: ${{ github.token }} + responseRequiredLabel: needs user action diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml index 0c2bc4148..badcf6cc9 100644 --- a/.github/workflows/snap.yml +++ b/.github/workflows/snap.yml @@ -64,7 +64,7 @@ jobs: uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8 - name: Telegram Desktop snap build. - run: sg lxd -c 'snap run snapcraft -v' + run: sg lxd -c 'snap run snapcraft --verbosity=debug' - name: Move artifact. if: env.UPLOAD_ARTIFACT == 'true' diff --git a/.github/workflows/waiting-for-answer.yml b/.github/workflows/waiting-for-answer.yml new file mode 100644 index 000000000..5e5ff87c9 --- /dev/null +++ b/.github/workflows/waiting-for-answer.yml @@ -0,0 +1,16 @@ +name: Waiting for answer. + +on: + issue_comment: + types: [created] + schedule: + - cron: '30 0 * * *' + +jobs: + waiting-for-answer: + runs-on: ubuntu-latest + steps: + - uses: lee-dohm/no-response@v0.5.0 + with: + token: ${{ github.token }} + responseRequiredLabel: waiting for answer diff --git a/.gitmodules b/.gitmodules index 64928f01b..a2a91a3f1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -82,12 +82,6 @@ [submodule "Telegram/ThirdParty/dispatch"] path = Telegram/ThirdParty/dispatch url = https://github.com/apple/swift-corelibs-libdispatch -[submodule "Telegram/ThirdParty/plasma-wayland-protocols"] - path = Telegram/ThirdParty/plasma-wayland-protocols - url = https://github.com/KDE/plasma-wayland-protocols.git -[submodule "Telegram/ThirdParty/wayland-protocols"] - path = Telegram/ThirdParty/wayland-protocols - url = https://github.com/gitlab-freedesktop-mirrors/wayland-protocols.git [submodule "Telegram/ThirdParty/kimageformats"] path = Telegram/ThirdParty/kimageformats url = https://github.com/KDE/kimageformats.git @@ -97,9 +91,6 @@ [submodule "Telegram/ThirdParty/cld3"] path = Telegram/ThirdParty/cld3 url = https://github.com/google/cld3.git -[submodule "Telegram/ThirdParty/wayland"] - path = Telegram/ThirdParty/wayland - url = https://github.com/gitlab-freedesktop-mirrors/wayland.git [submodule "Telegram/ThirdParty/libprisma"] path = Telegram/ThirdParty/libprisma url = https://github.com/desktop-app/libprisma.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 6971dbc8d..b4c265e24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,7 +62,7 @@ if (NOT DESKTOP_APP_USE_PACKAGED) if (WIN32) set(qt_version 5.15.13) elseif (APPLE) - set(qt_version 6.2.7) + set(qt_version 6.2.8) endif() endif() include(cmake/external/qt/package.cmake) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 536fafb0b..11f1fc9a3 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -342,6 +342,8 @@ PRIVATE boxes/local_storage_box.h boxes/max_invite_box.cpp boxes/max_invite_box.h + boxes/moderate_messages_box.cpp + boxes/moderate_messages_box.h boxes/peer_list_box.cpp boxes/peer_list_box.h boxes/peer_list_controllers.cpp @@ -529,10 +531,14 @@ PRIVATE data/business/data_business_info.h data/business/data_shortcut_messages.cpp data/business/data_shortcut_messages.h + data/components/recent_peers.cpp + data/components/recent_peers.h data/components/scheduled_messages.cpp data/components/scheduled_messages.h data/components/sponsored_messages.cpp data/components/sponsored_messages.h + data/components/top_peers.cpp + data/components/top_peers.h data/notify/data_notify_settings.cpp data/notify/data_notify_settings.h data/notify/data_peer_notify_settings.cpp @@ -677,6 +683,18 @@ PRIVATE data/data_wall_paper.h data/data_web_page.cpp data/data_web_page.h + dialogs/ui/dialogs_layout.cpp + dialogs/ui/dialogs_layout.h + dialogs/ui/dialogs_message_view.cpp + dialogs/ui/dialogs_message_view.h + dialogs/ui/dialogs_stories_content.cpp + dialogs/ui/dialogs_stories_content.h + dialogs/ui/dialogs_suggestions.cpp + dialogs/ui/dialogs_suggestions.h + dialogs/ui/dialogs_topics_view.cpp + dialogs/ui/dialogs_topics_view.h + dialogs/ui/dialogs_video_userpic.cpp + dialogs/ui/dialogs_video_userpic.h dialogs/dialogs_entry.cpp dialogs/dialogs_entry.h dialogs/dialogs_indexed_list.cpp @@ -699,16 +717,6 @@ PRIVATE dialogs/dialogs_search_tags.h dialogs/dialogs_widget.cpp dialogs/dialogs_widget.h - dialogs/ui/dialogs_layout.cpp - dialogs/ui/dialogs_layout.h - dialogs/ui/dialogs_message_view.cpp - dialogs/ui/dialogs_message_view.h - dialogs/ui/dialogs_stories_content.cpp - dialogs/ui/dialogs_stories_content.h - dialogs/ui/dialogs_topics_view.cpp - dialogs/ui/dialogs_topics_view.h - dialogs/ui/dialogs_video_userpic.cpp - dialogs/ui/dialogs_video_userpic.h editor/color_picker.cpp editor/color_picker.h editor/controllers/controllers.h @@ -1268,11 +1276,6 @@ PRIVATE payments/payments_checkout_process.h payments/payments_form.cpp payments/payments_form.h - platform/linux/linux_wayland_integration_dummy.cpp - platform/linux/linux_wayland_integration.cpp - platform/linux/linux_wayland_integration.h - platform/linux/linux_xdp_open_with_dialog.cpp - platform/linux/linux_xdp_open_with_dialog.h platform/linux/file_utilities_linux.cpp platform/linux/file_utilities_linux.h platform/linux/launcher_linux.cpp @@ -1625,16 +1628,6 @@ if (NOT build_winstore) ) endif() -if (DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION) - remove_target_sources(Telegram ${src_loc} - platform/linux/linux_wayland_integration.cpp - ) -else() - remove_target_sources(Telegram ${src_loc} - platform/linux/linux_wayland_integration_dummy.cpp - ) -endif() - if (DESKTOP_APP_USE_PACKAGED) remove_target_sources(Telegram ${src_loc} platform/mac/mac_iconv_helper.c @@ -1774,19 +1767,6 @@ else() desktop-app::external_xcb ) endif() - - if (NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION) - qt_generate_wayland_protocol_client_sources(Telegram - FILES - ${third_party_loc}/wayland/protocol/wayland.xml - ${third_party_loc}/plasma-wayland-protocols/src/protocols/plasma-shell.xml - ) - - target_link_libraries(Telegram - PRIVATE - desktop-app::external_wayland_client - ) - endif() endif() if (build_macstore) diff --git a/Telegram/Resources/animations/noresults.tgs b/Telegram/Resources/animations/noresults.tgs new file mode 100644 index 000000000..62a21279f Binary files /dev/null and b/Telegram/Resources/animations/noresults.tgs differ diff --git a/Telegram/Resources/animations/search.tgs b/Telegram/Resources/animations/search.tgs new file mode 100644 index 000000000..db635fb57 Binary files /dev/null and b/Telegram/Resources/animations/search.tgs differ diff --git a/Telegram/Resources/icons/chat/live_location_long.png b/Telegram/Resources/icons/chat/live_location_long.png new file mode 100644 index 000000000..847930f54 Binary files /dev/null and b/Telegram/Resources/icons/chat/live_location_long.png differ diff --git a/Telegram/Resources/icons/chat/live_location_long@2x.png b/Telegram/Resources/icons/chat/live_location_long@2x.png new file mode 100644 index 000000000..d4c018200 Binary files /dev/null and b/Telegram/Resources/icons/chat/live_location_long@2x.png differ diff --git a/Telegram/Resources/icons/chat/live_location_long@3x.png b/Telegram/Resources/icons/chat/live_location_long@3x.png new file mode 100644 index 000000000..3a8cd36b5 Binary files /dev/null and b/Telegram/Resources/icons/chat/live_location_long@3x.png differ diff --git a/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow.png b/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow.png new file mode 100644 index 000000000..19f8e0b15 Binary files /dev/null and b/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow.png differ diff --git a/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow@2x.png b/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow@2x.png new file mode 100644 index 000000000..ac695b71d Binary files /dev/null and b/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow@2x.png differ diff --git a/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow@3x.png b/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow@3x.png new file mode 100644 index 000000000..b05f0f35f Binary files /dev/null and b/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow@3x.png differ diff --git a/Telegram/Resources/icons/menu/fonts.png b/Telegram/Resources/icons/menu/fonts.png new file mode 100644 index 000000000..a82aa2166 Binary files /dev/null and b/Telegram/Resources/icons/menu/fonts.png differ diff --git a/Telegram/Resources/icons/menu/fonts@2x.png b/Telegram/Resources/icons/menu/fonts@2x.png new file mode 100644 index 000000000..5d735d58b Binary files /dev/null and b/Telegram/Resources/icons/menu/fonts@2x.png differ diff --git a/Telegram/Resources/icons/menu/fonts@3x.png b/Telegram/Resources/icons/menu/fonts@3x.png new file mode 100644 index 000000000..0aa3679f3 Binary files /dev/null and b/Telegram/Resources/icons/menu/fonts@3x.png differ diff --git a/Telegram/Resources/iv_html/page.css b/Telegram/Resources/iv_html/page.css index 2763760f2..9c0ab832f 100644 --- a/Telegram/Resources/iv_html/page.css +++ b/Telegram/Resources/iv_html/page.css @@ -134,9 +134,32 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover { .page-slide { position: relative; width: 100%; + min-height: 100%; margin-left: 0%; transition: margin 240ms ease-in-out; } +.page-footer { + height: 32px; + margin-top: -32px; + background: var(--td-window-bg-over); +} +.page-footer .content { + padding: 3px 18px; + font-size: 15px; + color: var(--td-window-sub-text-fg); + text-align: center; +} +.page-footer .wrong { + position: relative; + padding: 5px; + margin: -5px; + color: var(--td-window-sub-text-fg); + text-decoration: none; + cursor: pointer; +} +.page-footer .wrong:hover { + text-decoration: underline; +} .hidden-left, .hidden-right { pointer-events: none; @@ -148,7 +171,7 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover { margin-left: 100%; } article { - padding-bottom: 12px; + padding-bottom: 40px; overflow-y: hidden; overflow-x: auto; white-space: pre-wrap; @@ -893,6 +916,9 @@ section.related a.related-link:after { right: 0; bottom: 0; } +section.related a.related-link:last-child:after { + border-bottom: 0px; +} section.related .related-link-url { display: block; font-size: 15px; @@ -1027,6 +1053,9 @@ section.channel > a > h4 { display: block; margin: 0 auto; } +.media-outer { + margin-bottom: 16px; +} .photo-wrap, .video-wrap { width: 100%; diff --git a/Telegram/Resources/iv_html/page.js b/Telegram/Resources/iv_html/page.js index 053a6a0ab..bae02fe48 100644 --- a/Telegram/Resources/iv_html/page.js +++ b/Telegram/Resources/iv_html/page.js @@ -26,7 +26,7 @@ var IV = { } target = target.parentNode; } - if (!target || !target.hasAttribute('href')) { + if (!target || (context === '' && !target.hasAttribute('href'))) { return; } var base = document.createElement('A'); @@ -413,9 +413,12 @@ var IV = { var article = function (el) { return el.getElementsByTagName('article')[0]; }; - var from = article(IV.findPageScroll()); - var to = article(IV.makeScrolledContent(data.html)); - morphdom(from, to, { + var footer = function (el) { + return el.getElementsByClassName('page-footer')[0]; + }; + var from = IV.findPageScroll(); + var to = IV.makeScrolledContent(data.html); + morphdom(article(from), article(to), { onBeforeElUpdated: function (fromEl, toEl) { if (fromEl.classList.contains('video') && toEl.classList.contains('video') @@ -439,6 +442,7 @@ var IV = { return !fromEl.isEqualNode(toEl); } }); + morphdom(footer(from), footer(to)); IV.initMedia(); eval(data.js); }, @@ -477,9 +481,7 @@ var IV = { var result = document.createElement('div'); result.className = 'page-scroll'; result.tabIndex = '-1'; - result.innerHTML = '
' - + html - + '
'; + result.innerHTML = html.trim(); result.onscroll = IV.frameScrolled; return result; }, diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 2c2009edd..34e5cfaed 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -730,6 +730,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_angle_backend_d3d11on12" = "D3D11on12"; "lng_settings_angle_backend_opengl" = "OpenGL"; "lng_settings_angle_backend_disabled" = "Disabled"; +"lng_settings_top_peers_title" = "Frequent contacts"; +"lng_settings_top_peers_suggest" = "Suggest frequent contacts"; +"lng_settings_top_peers_about" = "Display people you message frequently at the top of the search section for quick access."; "lng_settings_sensitive_title" = "Sensitive content"; "lng_settings_sensitive_disable_filtering" = "Disable filtering"; "lng_settings_sensitive_about" = "Display sensitive media in public channels on all your Telegram devices."; @@ -840,6 +843,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_auto_night_mode_on" = "System"; "lng_settings_auto_night_warning" = "You have enabled auto-night mode. If you want to change the dark mode settings, you'll need to disable it first."; "lng_settings_auto_night_disable" = "Disable"; +"lng_settings_font_family" = "Font family"; "lng_settings_color_title" = "Color preview"; "lng_settings_color_reply" = "Reply to your message"; @@ -1492,6 +1496,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_peer_reactions_none_about" = "Members of the group can't add any reactions to messages."; "lng_manage_peer_reactions_some_title" = "Only allow these reactions"; "lng_manage_peer_reactions_available" = "Available reactions"; +"lng_manage_peer_reactions_available_ph" = "Add reactions..."; "lng_manage_peer_reactions_own" = "You can also {link} emoji packs and use them as reactions."; "lng_manage_peer_reactions_own_link" = "create your own"; "lng_manage_peer_reactions_level#one" = "Your channel needs to reach level **{count}** to use **{same_count}** custom reaction."; @@ -1500,6 +1505,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_peer_reactions_boost_link" = "here"; "lng_manage_peer_reactions_limit" = "Channels can't have more custom reactions."; +"lng_manage_peer_reactions_max_title" = "Maximum number of reactions"; +"lng_manage_peer_reactions_max_slider#one" = "{count} reaction per post"; +"lng_manage_peer_reactions_max_slider#other" = "{count} reactions per post"; +"lng_manage_peer_reactions_max_about" = "Limit the number of different reactions that can be added to a post, including already published ones."; + "lng_manage_peer_antispam" = "Aggressive Anti-Spam"; "lng_manage_peer_antispam_about" = "Telegram will filter more spam but may occasionally affect ordinary messages. You can report False Positives in Recent Actions."; "lng_manage_peer_antispam_not_enough#one" = "Aggressive filtering can be enabled only in groups with more than **{count} member**."; @@ -2269,6 +2279,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_business_about_chat_intro" = "Customize the message people see before they start a chat with you."; "lng_business_subtitle_chat_links" = "Links to Chat"; "lng_business_about_chat_links" = "Create links that start a chat with you, suggesting the first message."; +"lng_business_subtitle_sponsored" = "Ads in Channels"; +"lng_business_button_sponsored" = "Do Not Hide Ads"; +"lng_business_about_sponsored" = "As a Premium subscriber, you don’t see any ads on Telegram, but you can turn them on, for example, to view your own ads that you launched on the {link}"; +"lng_business_about_sponsored_link" = "Telegram Ad Platform {emoji}"; +"lng_business_about_sponsored_url" = "https://ads.telegram.org"; "lng_location_title" = "Location"; "lng_location_about" = "Display the location of your business on your account."; @@ -2839,7 +2854,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_in_dlg_audio_count#other" = "{count} audio"; "lng_ban_user" = "Ban User"; +"lng_ban_users" = "Ban users"; +"lng_restrict_users" = "Restrict users"; "lng_delete_all_from_user" = "Delete all from {user}"; +"lng_delete_all_from_users" = "Delete all from users"; +"lng_restrict_user_part" = "Partially restrict this user {emoji}"; +"lng_restrict_users_part" = "Partially restrict users {emoji}"; +"lng_restrict_user_full" = "Fully ban this user {emoji}"; +"lng_restrict_users_full" = "Fully ban users {emoji}"; +"lng_restrict_users_part_single_header" = "What can this user do?"; +"lng_restrict_users_part_header#one" = "What can {count} selected user do?"; +"lng_restrict_users_part_header#other" = "What can {count} selected users do?"; "lng_report_spam" = "Report Spam"; "lng_report_spam_and_leave" = "Report spam and leave"; "lng_report_spam_done" = "Thank you for your report."; @@ -3067,6 +3092,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_unread_bar_some" = "Unread messages"; "lng_maps_point" = "Location"; +"lng_live_location" = "Live Location"; +"lng_live_location_now" = "updated just now"; +"lng_live_location_minutes#one" = "updated {count} minute ago"; +"lng_live_location_minutes#other" = "updated {count} minutes ago"; +"lng_live_location_hours#one" = "updated {count} hour ago"; +"lng_live_location_hours#other" = "updated {count} hours ago"; +"lng_live_location_today" = "updated today at {time}"; +"lng_live_location_yesterday" = "updated yesterday at {time}"; +"lng_live_location_date_time" = "updated {date} at {time}"; "lng_save_photo" = "Save image"; "lng_save_video" = "Save video"; "lng_save_audio_file" = "Save audio file"; @@ -3410,6 +3444,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_mediaview_forward" = "Forward"; "lng_mediaview_delete" = "Delete"; "lng_mediaview_save_to_profile" = "Save to Profile"; +"lng_mediaview_pin_story_done" = "Story pinned"; +"lng_mediaview_pin_story_about" = "Now it will be always shown on the top."; +"lng_mediaview_pin_stories_done#one" = "{count} story pinned"; +"lng_mediaview_pin_stories_done#other" = "{count} stories pinned"; +"lng_mediaview_pin_stories_about#one" = "Now it will be always shown on the top."; +"lng_mediaview_pin_stories_about#other" = "Now they will be always shown on the top."; +"lng_mediaview_unpin_story_done" = "Story unpinned."; +"lng_mediaview_unpin_stories_done#one" = "{count} story unpinned"; +"lng_mediaview_unpin_stories_done#other" = "{count} stories unpinned"; +"lng_mediaview_pin_limit#one" = "You can't pin more than {count} story."; +"lng_mediaview_pin_limit#other" = "You can't pin more than {count} stories."; "lng_mediaview_archive_story" = "Archive Story"; "lng_mediaview_photos_all" = "View all photos"; "lng_mediaview_files_all" = "View all files"; @@ -4704,6 +4749,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_view_button_boost" = "Boost"; "lng_view_button_giftcode" = "Open"; "lng_view_button_iv" = "Instant View"; +"lng_view_button_stickerset" = "View stickers"; +"lng_view_button_emojipack" = "View emoji"; "lng_sponsored_hide_ads" = "Hide"; "lng_sponsored_title" = "What are sponsored messages?"; @@ -5073,6 +5120,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_iv_share" = "Share"; "lng_iv_join_channel" = "Join"; "lng_iv_window_title" = "Instant View"; +"lng_iv_wrong_layout" = "Wrong layout?"; "lng_limit_download_title" = "Download speed limited"; "lng_limit_download_subscribe" = "Subscribe to {link} and increase download speed {increase}."; @@ -5087,6 +5135,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_limit_upload_increase_speed" = "by **{percent}**"; "lng_limit_upload_subscribe_link" = "Telegram Premium"; +"lng_recent_frequent" = "Frequent contacts"; +"lng_recent_frequent_all" = "Show all"; +"lng_recent_frequent_collapse" = "Collapse"; +"lng_recent_title" = "Recent"; +"lng_recent_clear" = "Clear"; +"lng_recent_clear_sure" = "Do you want to clear your search history?"; +"lng_recent_remove" = "Remove from Recent"; +"lng_recent_clear_all" = "Clear all"; +"lng_recent_hide_top" = "Remove all & Disable"; +"lng_recent_hide_sure" = "Are you sure you want to clear and disable frequent contacts list?\n\nYou can always turn this feature back on in Settings > Privacy > Suggest Frequent Contacts."; +"lng_recent_hide_button" = "Hide"; +"lng_recent_none" = "Recent search results\nwill appear here."; +"lng_recent_chats" = "Chats"; +"lng_recent_channels" = "Channels"; +"lng_channels_none_title" = "No channels yet..."; +"lng_channels_none_about" = "You are not currently subscribed to any channels."; +"lng_channels_your_title" = "Channels you joined"; +"lng_channels_your_more" = "Show more"; +"lng_channels_your_less" = "Show less"; +"lng_channels_recommended" = "Recommended channels"; + +"lng_font_box_title" = "Choose font family"; +"lng_font_default" = "Default"; +"lng_font_system" = "System font"; +"lng_font_not_found" = "Font not found."; + // Wnd specific "lng_wnd_choose_program_menu" = "Choose Default Program..."; diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc index 5b72c461f..16920ae4d 100644 --- a/Telegram/Resources/qrc/telegram/animations.qrc +++ b/Telegram/Resources/qrc/telegram/animations.qrc @@ -24,5 +24,7 @@ ../../animations/chat_link.tgs ../../animations/collectible_username.tgs ../../animations/collectible_phone.tgs + ../../animations/search.tgs + ../../animations/noresults.tgs diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 734a63bc4..0021dd5de 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="5.0.1.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 481b83cfb..e7d2777da 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,16,8,0 - PRODUCTVERSION 4,16,8,0 + FILEVERSION 5,0,1,0 + PRODUCTVERSION 5,0,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Radolyn Labs" VALUE "FileDescription", "AyuGram Desktop" - VALUE "FileVersion", "4.16.8.0" + VALUE "FileVersion", "5.0.1.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "AyuGram Desktop" - VALUE "ProductVersion", "4.16.8.0" + VALUE "ProductVersion", "5.0.1.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 208fdf399..d3e2f5f5b 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,16,8,0 - PRODUCTVERSION 4,16,8,0 + FILEVERSION 5,0,1,0 + PRODUCTVERSION 5,0,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Radolyn Labs" VALUE "FileDescription", "AyuGram Desktop Updater" - VALUE "FileVersion", "4.16.8.0" + VALUE "FileVersion", "5.0.1.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "AyuGram Desktop" - VALUE "ProductVersion", "4.16.8.0" + VALUE "ProductVersion", "5.0.1.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/api/api_chat_participants.cpp b/Telegram/SourceFiles/api/api_chat_participants.cpp index 745e0b883..a4cd0c367 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.cpp +++ b/Telegram/SourceFiles/api/api_chat_participants.cpp @@ -212,7 +212,7 @@ void ApplyBotsList( } [[nodiscard]] ChatParticipants::Channels ParseSimilar( - not_null channel, + not_null session, const MTPmessages_Chats &chats) { auto result = ChatParticipants::Channels(); std::vector>(); @@ -220,13 +220,13 @@ void ApplyBotsList( const auto &list = data.vchats().v; result.list.reserve(list.size()); for (const auto &chat : list) { - const auto peer = channel->owner().processChat(chat); + const auto peer = session->data().processChat(chat); if (const auto channel = peer->asChannel()) { result.list.push_back(channel); } } if constexpr (MTPDmessages_chatsSlice::Is()) { - if (channel->session().premiumPossible()) { + if (session->premiumPossible()) { result.more = data.vcount().v - data.vchats().v.size(); } } @@ -234,6 +234,12 @@ void ApplyBotsList( return result; } +[[nodiscard]] ChatParticipants::Channels ParseSimilar( + not_null channel, + const MTPmessages_Chats &chats) { + return ParseSimilar(&channel->session(), chats); +} + } // namespace ChatParticipant::ChatParticipant( @@ -351,7 +357,8 @@ QString ChatParticipant::rank() const { } ChatParticipants::ChatParticipants(not_null api) -: _api(&api->instance()) { +: _session(&api->session()) +, _api(&api->instance()) { } void ChatParticipants::requestForAdd( @@ -585,6 +592,33 @@ ChatParticipants::Parsed ChatParticipants::ParseRecent( return result; } +void ChatParticipants::Restrict( + not_null channel, + not_null participant, + ChatRestrictionsInfo oldRights, + ChatRestrictionsInfo newRights, + Fn onDone, + Fn onFail) { + channel->session().api().request(MTPchannels_EditBanned( + channel->inputChannel, + participant->input, + MTP_chatBannedRights( + MTP_flags(MTPDchatBannedRights::Flags::from_raw( + uint32(newRights.flags))), + MTP_int(newRights.until)) + )).done([=](const MTPUpdates &result) { + channel->session().api().applyUpdates(result); + channel->applyEditBanned(participant, oldRights, newRights); + if (onDone) { + onDone(); + } + }).fail([=] { + if (onFail) { + onFail(); + } + }).send(); +} + void ChatParticipants::requestSelf(not_null channel) { if (_selfParticipantRequests.contains(channel)) { return; @@ -730,8 +764,11 @@ void ChatParticipants::loadSimilarChannels(not_null channel) { return; } } + using Flag = MTPchannels_GetChannelRecommendations::Flag; _similar[channel].requestId = _api.request( - MTPchannels_GetChannelRecommendations(channel->inputChannel) + MTPchannels_GetChannelRecommendations( + MTP_flags(Flag::f_channel), + channel->inputChannel) ).done([=](const MTPmessages_Chats &result) { auto &similar = _similar[channel]; similar.requestId = 0; @@ -766,4 +803,29 @@ auto ChatParticipants::similarLoaded() const return _similarLoaded.events(); } +void ChatParticipants::loadRecommendations() { + if (_recommendationsLoaded.current() || _recommendations.requestId) { + return; + } + _recommendations.requestId = _api.request( + MTPchannels_GetChannelRecommendations( + MTP_flags(0), + MTP_inputChannelEmpty()) + ).done([=](const MTPmessages_Chats &result) { + _recommendations.requestId = 0; + auto parsed = ParseSimilar(_session, result); + _recommendations.channels = std::move(parsed); + _recommendations.channels.more = 0; + _recommendationsLoaded = true; + }).send(); +} + +const ChatParticipants::Channels &ChatParticipants::recommendations() const { + return _recommendations.channels; +} + +rpl::producer<> ChatParticipants::recommendationsLoaded() const { + return _recommendationsLoaded.changes() | rpl::to_empty; +} + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_chat_participants.h b/Telegram/SourceFiles/api/api_chat_participants.h index b8fc9fc67..ad5aa930f 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.h +++ b/Telegram/SourceFiles/api/api_chat_participants.h @@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class ApiWrap; class ChannelData; +namespace Main { +class Session; +} // namespace Main + namespace Ui { class Show; } // namespace Ui @@ -96,6 +100,13 @@ public: static Parsed ParseRecent( not_null channel, const TLMembers &data); + static void Restrict( + not_null channel, + not_null participant, + ChatRestrictionsInfo oldRights, + ChatRestrictionsInfo newRights, + Fn onDone, + Fn onFail); void add( std::shared_ptr show, not_null peer, @@ -134,12 +145,18 @@ public: [[nodiscard]] auto similarLoaded() const -> rpl::producer>; + void loadRecommendations(); + [[nodiscard]] const Channels &recommendations() const; + [[nodiscard]] rpl::producer<> recommendationsLoaded() const; + private: struct SimilarChannels { Channels channels; mtpRequestId requestId = 0; }; + const not_null _session; + MTP::Sender _api; using PeerRequests = base::flat_map; @@ -165,6 +182,9 @@ private: base::flat_map, SimilarChannels> _similar; rpl::event_stream> _similarLoaded; + SimilarChannels _recommendations; + rpl::variable _recommendationsLoaded = false; + }; } // namespace Api diff --git a/Telegram/SourceFiles/api/api_confirm_phone.cpp b/Telegram/SourceFiles/api/api_confirm_phone.cpp index 0dd1ae9a7..f4788e128 100644 --- a/Telegram/SourceFiles/api/api_confirm_phone.cpp +++ b/Telegram/SourceFiles/api/api_confirm_phone.cpp @@ -62,6 +62,10 @@ void ConfirmPhone::resolve( return bad("FirebaseSms"); }, [&](const MTPDauth_sentCodeTypeEmailCode &) { return bad("EmailCode"); + }, [&](const MTPDauth_sentCodeTypeSmsWord &) { + return bad("SmsWord"); + }, [&](const MTPDauth_sentCodeTypeSmsPhrase &) { + return bad("SmsPhrase"); }, [&](const MTPDauth_sentCodeTypeSetUpEmailRequired &) { return bad("SetUpEmailRequired"); }); diff --git a/Telegram/SourceFiles/api/api_premium.cpp b/Telegram/SourceFiles/api/api_premium.cpp index 5257feb2d..6ff4aa130 100644 --- a/Telegram/SourceFiles/api/api_premium.cpp +++ b/Telegram/SourceFiles/api/api_premium.cpp @@ -602,6 +602,41 @@ bool PremiumGiftCodeOptions::giveawayGiftsPurchaseAvailable() const { false); } +SponsoredToggle::SponsoredToggle(not_null session) +: _api(&session->api().instance()) { +} + +rpl::producer SponsoredToggle::toggled() { + return [=](auto consumer) { + auto lifetime = rpl::lifetime(); + + _api.request(MTPusers_GetFullUser( + MTP_inputUserSelf() + )).done([=](const MTPusers_UserFull &result) { + consumer.put_next_copy( + result.data().vfull_user().data().is_sponsored_enabled()); + }).fail([=] { consumer.put_next(false); }).send(); + + return lifetime; + }; +} + +rpl::producer SponsoredToggle::setToggled(bool v) { + return [=](auto consumer) { + auto lifetime = rpl::lifetime(); + + _api.request(MTPaccount_ToggleSponsoredMessages( + MTP_bool(v) + )).done([=] { + consumer.put_done(); + }).fail([=](const MTP::Error &error) { + consumer.put_error_copy(error.type()); + }).send(); + + return lifetime; + }; +} + RequirePremiumState ResolveRequiresPremiumToWrite( not_null peer, History *maybeHistory) { diff --git a/Telegram/SourceFiles/api/api_premium.h b/Telegram/SourceFiles/api/api_premium.h index 9bacca92d..83423fcd2 100644 --- a/Telegram/SourceFiles/api/api_premium.h +++ b/Telegram/SourceFiles/api/api_premium.h @@ -216,6 +216,18 @@ private: }; +class SponsoredToggle final { +public: + explicit SponsoredToggle(not_null session); + + [[nodiscard]] rpl::producer toggled(); + [[nodiscard]] rpl::producer setToggled(bool); + +private: + MTP::Sender _api; + +}; + enum class RequirePremiumState { Unknown, Yes, diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp index 7139c5ebb..ce1966623 100644 --- a/Telegram/SourceFiles/api/api_statistics.cpp +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -760,14 +760,14 @@ rpl::producer EarnStatistics::request() { channel()->inputChannel )).done([=](const MTPstats_BroadcastRevenueStats &result) { const auto &data = result.data(); - + const auto &balances = data.vbalances().data(); _data = Data::EarnStatistics{ .topHoursGraph = StatisticalGraphFromTL( data.vtop_hours_graph()), .revenueGraph = StatisticalGraphFromTL(data.vrevenue_graph()), - .currentBalance = data.vcurrent_balance().v, - .availableBalance = data.vavailable_balance().v, - .overallRevenue = data.voverall_revenue().v, + .currentBalance = balances.vcurrent_balance().v, + .availableBalance = balances.vavailable_balance().v, + .overallRevenue = balances.voverall_revenue().v, .usdRate = data.vusd_rate().v, }; diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 97b2f7529..8821bb245 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtproto_dc_options.h" #include "data/business/data_shortcut_messages.h" #include "data/components/scheduled_messages.h" +#include "data/components/top_peers.h" #include "data/notify/data_notify_settings.h" #include "data/stickers/data_stickers.h" #include "data/data_saved_messages.h" @@ -1585,6 +1586,11 @@ void Updates::feedUpdate(const MTPUpdate &update) { } else { if (existing) { existing->destroy(); + } else { + // Not the server-side date, but close enough. + session().topPeers().increment( + local->history()->peer, + local->date()); } local->setRealId(d.vid().v); } diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp index a112c478a..828b2d095 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp @@ -940,7 +940,7 @@ int BackgroundPreviewBox::textsTop() const { - st::historyPaddingBottom - (_service ? _service->height() : 0) - _text1->height() - - (forChannel() ? _text2->height() : 0); + - (forChannel() ? 0 : _text2->height()); } QRect BackgroundPreviewBox::radialRect() const { diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index c181168d1..87fa1556f 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -546,7 +546,7 @@ rightsToggle: Toggle(defaultToggle) { vsize: 0px; vshift: 1px; stroke: 2px; - duration: 120; + duration: universalDuration; } rightsButton: SettingsButton(defaultSettingsButton) { @@ -691,6 +691,10 @@ createPollOptionField: InputField(createPollField) { placeholderMargins: margins(2px, 0px, 2px, 0px); heightMax: 68px; } +createPollOptionFieldPremium: InputField(createPollOptionField) { + textMargins: margins(22px, 11px, 68px, 11px); +} +createPollOptionFieldPremiumEmojiPosition: point(15px, -1px); createPollSolutionField: InputField(createPollField) { textMargins: margins(0px, 4px, 0px, 4px); border: 1px; @@ -704,7 +708,7 @@ createPollOptionRemove: CrossButton { cross: CrossAnimation { size: 22px; skip: 6px; - stroke: 1.; + stroke: 1.5; minScale: 0.3; } crossFg: boxTitleCloseFg; @@ -718,6 +722,7 @@ createPollOptionRemove: CrossButton { } } createPollOptionRemovePosition: point(11px, 9px); +createPollOptionEmojiPositionSkip: 4px; createPollWarning: FlatLabel(defaultFlatLabel) { textFg: windowSubTextFg; palette: TextPalette(defaultTextPalette) { @@ -1074,3 +1079,23 @@ collectibleBox: Box(defaultBox) { buttonHeight: 36px; button: collectibleCopy; } + +moderateBoxUserpic: UserpicButton(defaultUserpicButton) { + size: size(34px, 42px); + photoSize: 34px; + photoPosition: point(0px, 4px); +} +moderateBoxExpand: icon {{ "chat/reply_type_group", boxTextFg }}; +moderateBoxExpandHeight: 20px; +moderateBoxExpandRight: 10px; +moderateBoxExpandInnerSkip: 2px; +moderateBoxExpandFont: font(11px); +moderateBoxExpandToggleSize: 4px; +moderateBoxExpandToggleFourStrokes: 3px; +moderateBoxExpandIcon: icon{{ "info/edit/expand_arrow_small-flip_vertical", windowActiveTextFg }}; +moderateBoxExpandIconDown: icon{{ "info/edit/expand_arrow_small", windowActiveTextFg }}; +moderateBoxDividerLabel: FlatLabel(boxDividerLabel) { + palette: TextPalette(defaultTextPalette) { + selectLinkFg: windowActiveTextFg; + } +} diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp index 373899be4..1d02b5e52 100644 --- a/Telegram/SourceFiles/boxes/create_poll_box.cpp +++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp @@ -7,33 +7,41 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/create_poll_box.h" -#include "lang/lang_keys.h" -#include "data/data_poll.h" -#include "ui/toast/toast.h" -#include "ui/wrap/vertical_layout.h" -#include "ui/wrap/slide_wrap.h" -#include "ui/wrap/fade_wrap.h" -#include "ui/widgets/fields/input_field.h" -#include "ui/widgets/shadow.h" -#include "ui/widgets/labels.h" -#include "ui/widgets/buttons.h" -#include "ui/widgets/checkbox.h" -#include "ui/text/text_utilities.h" -#include "ui/vertical_list.h" -#include "main/main_session.h" -#include "core/application.h" -#include "core/core_settings.h" +#include "base/call_delayed.h" +#include "base/event_filter.h" +#include "base/random.h" +#include "base/unique_qptr.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/message_field.h" -#include "menu/menu_send.h" +#include "chat_helpers/tabbed_panel.h" +#include "chat_helpers/tabbed_selector.h" +#include "core/application.h" +#include "core/core_settings.h" +#include "data/data_poll.h" +#include "data/data_user.h" +#include "data/stickers/data_custom_emoji.h" #include "history/view/history_view_schedule_box.h" -#include "base/unique_qptr.h" -#include "base/event_filter.h" -#include "base/call_delayed.h" -#include "base/random.h" +#include "lang/lang_keys.h" +#include "main/main_session.h" +#include "menu/menu_send.h" +#include "ui/controls/emoji_button.h" +#include "ui/rect.h" +#include "ui/text/text_utilities.h" +#include "ui/toast/toast.h" +#include "ui/vertical_list.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/fields/input_field.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/shadow.h" +#include "ui/wrap/fade_wrap.h" +#include "ui/wrap/slide_wrap.h" +#include "ui/wrap/vertical_layout.h" #include "window/window_session_controller.h" -#include "styles/style_layers.h" #include "styles/style_boxes.h" +#include "styles/style_chat_helpers.h" // defaultComposeFiles. +#include "styles/style_layers.h" +#include "styles/style_settings.h" namespace { @@ -46,12 +54,107 @@ constexpr auto kSolutionLimit = 200; constexpr auto kWarnSolutionLimit = 60; constexpr auto kErrorLimit = 99; +[[nodiscard]] not_null AddEmojiToggleToField( + not_null field, + not_null box, + not_null controller, + not_null emojiPanel, + QPoint shift) { + const auto emojiToggle = Ui::CreateChild( + field->parentWidget(), + st::defaultComposeFiles.emoji); + const auto fade = Ui::CreateChild( + emojiToggle, + emojiToggle, + 0.5); + { + const auto fadeTarget = Ui::CreateChild(emojiToggle); + fadeTarget->resize(emojiToggle->size()); + fadeTarget->paintRequest( + ) | rpl::start_with_next([=](const QRect &rect) { + auto p = QPainter(fadeTarget); + if (fade->animating()) { + p.fillRect(fadeTarget->rect(), st::boxBg); + } + fade->paint(p); + }, fadeTarget->lifetime()); + rpl::single(false) | rpl::then( + field->focusedChanges() + ) | rpl::start_with_next([=](bool shown) { + if (shown) { + fade->fadeIn(st::universalDuration); + } else { + fade->fadeOut(st::universalDuration); + } + }, emojiToggle->lifetime()); + fade->fadeOut(1); + fade->finish(); + } + + + const auto outer = box->getDelegate()->outerContainer(); + const auto allow = [](not_null) { return true; }; + InitMessageFieldHandlers( + controller, + field, + Window::GifPauseReason::Layer, + allow); + Ui::Emoji::SuggestionsController::Init( + outer, + field, + &controller->session(), + Ui::Emoji::SuggestionsController::Options{ + .suggestCustomEmoji = true, + .allowCustomWithoutPremium = allow, + }); + const auto updateEmojiPanelGeometry = [=] { + const auto parent = emojiPanel->parentWidget(); + const auto global = emojiToggle->mapToGlobal({ 0, 0 }); + const auto local = parent->mapFromGlobal(global); + const auto right = local.x() + emojiToggle->width() * 3; + const auto isDropDown = local.y() < parent->height() / 2; + emojiPanel->setDropDown(isDropDown); + if (isDropDown) { + emojiPanel->moveTopRight( + local.y() + emojiToggle->height(), + right); + } else { + emojiPanel->moveBottomRight(local.y(), right); + } + }; + rpl::combine( + box->sizeValue(), + field->geometryValue() + ) | rpl::start_with_next([=](QSize outer, QRect inner) { + emojiToggle->moveToLeft( + rect::right(inner) + shift.x(), + inner.y() + shift.y()); + emojiToggle->update(); + }, emojiToggle->lifetime()); + + emojiToggle->installEventFilter(emojiPanel); + emojiToggle->addClickHandler([=] { + updateEmojiPanelGeometry(); + emojiPanel->toggleAnimated(); + }); + const auto filterCallback = [=](not_null event) { + if (event->type() == QEvent::Enter) { + updateEmojiPanelGeometry(); + } + return base::EventFilterResult::Continue; + }; + base::install_event_filter(emojiToggle, filterCallback); + + return emojiToggle; +} + class Options { public: Options( - not_null outer, + not_null box, not_null container, - not_null session, + not_null controller, + ChatHelpers::TabbedPanel *emojiPanel, bool chooseCorrectEnabled); [[nodiscard]] bool hasOptions() const; @@ -140,9 +243,10 @@ private: [[nodiscard]] auto createChooseCorrectGroup() -> std::shared_ptr; - not_null _outer; + not_null _box; not_null _container; - const not_null _session; + const not_null _controller; + ChatHelpers::TabbedPanel * const _emojiPanel; std::shared_ptr _chooseCorrectGroup; int _position = 0; std::vector> _list; @@ -154,6 +258,7 @@ private: rpl::event_stream> _scrollToWidget; rpl::event_stream<> _backspaceInFront; rpl::event_stream<> _tabbed; + rpl::lifetime _emojiPanelLifetime; }; @@ -193,8 +298,9 @@ not_null CreateWarningLabel( if (value >= 0) { result->setText(QString::number(value)); } else { + constexpr auto kMinus = QChar(0x2212); result->setMarkedText(Ui::Text::Colorized( - QString::number(value))); + kMinus + QString::number(std::abs(value)))); } result->setVisible(shown); })); @@ -223,7 +329,9 @@ Options::Option::Option( , _field( Ui::CreateChild( _content.get(), - st::createPollOptionField, + session->user()->isPremium() + ? st::createPollOptionFieldPremium + : st::createPollOptionField, Ui::InputField::Mode::NoNewlines, tr::lng_polls_create_option_add())) { InitField(outer, _field, session); @@ -299,7 +407,7 @@ void Options::Option::createRemove() { const auto remove = Ui::CreateChild( field.get(), st::createPollOptionRemove); - remove->hide(anim::type::instant); + remove->show(anim::type::instant); const auto toggle = lifetime.make_state>(false); _removeAlways = lifetime.make_state>(false); @@ -309,6 +417,7 @@ void Options::Option::createRemove() { // Don't capture 'this'! Because Option is a value type. *toggle = !field->getLastText().isEmpty(); }, field->lifetime()); +#if 0 rpl::combine( toggle->value(), _removeAlways->value(), @@ -316,6 +425,7 @@ void Options::Option::createRemove() { ) | rpl::start_with_next([=](bool shown) { remove->toggle(shown, anim::type::normal); }, remove->lifetime()); +#endif field->widthValue( ) | rpl::start_with_next([=](int width) { @@ -456,10 +566,16 @@ void Options::Option::removePlaceholder() const { PollAnswer Options::Option::toPollAnswer(int index) const { Expects(index >= 0 && index < kMaxOptionsCount); + const auto text = field()->getTextWithTags(); + auto result = PollAnswer{ - field()->getLastText().trimmed(), - QByteArray(1, ('0' + index)) + TextWithEntities{ + .text = text.text, + .entities = TextUtilities::ConvertTextTagsToEntities(text.tags), + }, + QByteArray(1, ('0' + index)), }; + TextUtilities::Trim(result.text); result.correct = _correct ? _correct->entity()->Checkbox::checked() : false; return result; } @@ -469,13 +585,15 @@ rpl::producer Options::Option::removeClicks() const { } Options::Options( - not_null outer, + not_null box, not_null container, - not_null session, + not_null controller, + ChatHelpers::TabbedPanel *emojiPanel, bool chooseCorrectEnabled) -: _outer(outer) +: _box(box) , _container(container) -, _session(session) +, _controller(controller) +, _emojiPanel(emojiPanel) , _chooseCorrectGroup(chooseCorrectEnabled ? createChooseCorrectGroup() : nullptr) @@ -645,12 +763,40 @@ void Options::addEmptyOption() { (*(_list.end() - 2))->toggleRemoveAlways(true); } _list.push_back(std::make_unique