Compare commits
126 Commits
window_bla
...
v6.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87f873c8b6 | ||
|
|
a37528b377 | ||
|
|
51a58182ee | ||
|
|
cc4b768f54 | ||
|
|
b016be6eb5 | ||
|
|
881d73d4ab | ||
|
|
8859e352f9 | ||
|
|
5b0fc5b97b | ||
|
|
76cc59acab | ||
|
|
e792088ceb | ||
|
|
1dc70d5a8d | ||
|
|
e876a0f6bd | ||
|
|
50450de3e4 | ||
|
|
c8abc84c3c | ||
|
|
1f3996032c | ||
|
|
b868cfdca9 | ||
|
|
d51944e6a5 | ||
|
|
4419ba55e8 | ||
|
|
9e8ae54821 | ||
|
|
4ed266780a | ||
|
|
46886b4dcc | ||
|
|
1e2531f0b1 | ||
|
|
0e43fd4d00 | ||
|
|
10448bcc3d | ||
|
|
4f1c2788b8 | ||
|
|
dbaa7b5e67 | ||
|
|
6211b7733d | ||
|
|
0d6ea0845e | ||
|
|
b8e10fb34b | ||
|
|
b9c6e595d7 | ||
|
|
7d701d3e9c | ||
|
|
92d9c3c92b | ||
|
|
7007977891 | ||
|
|
9f5d24bbc9 | ||
|
|
2834a83eec | ||
|
|
eb82473395 | ||
|
|
4a33d3227e | ||
|
|
235456e18e | ||
|
|
1f9e532fbc | ||
|
|
8bc2d3184a | ||
|
|
9ef54d1218 | ||
|
|
7e48cb97b0 | ||
|
|
a226f0b58c | ||
|
|
65fd47a082 | ||
|
|
600b7d1d54 | ||
|
|
b8f4d4877e | ||
|
|
6a114e1275 | ||
|
|
5d3d8640e3 | ||
|
|
f09d2d22eb | ||
|
|
94ccd8f8b6 | ||
|
|
c96ce890d6 | ||
|
|
de55be397b | ||
|
|
1420714cae | ||
|
|
b15305bc69 | ||
|
|
db25b111e0 | ||
|
|
eeea9932ed | ||
|
|
c710e9a54d | ||
|
|
62613e7da1 | ||
|
|
946b597471 | ||
|
|
691c55bedd | ||
|
|
8387969467 | ||
|
|
90d21375c8 | ||
|
|
86daf2a9dc | ||
|
|
90473957cb | ||
|
|
3549c00141 | ||
|
|
1c1d13545b | ||
|
|
f1b3db89fb | ||
|
|
8c77baca6f | ||
|
|
36a40d97a7 | ||
|
|
1eb9b40607 | ||
|
|
05c10e3f57 | ||
|
|
0cf3325655 | ||
|
|
48525de714 | ||
|
|
7e15722eab | ||
|
|
719c209c7b | ||
|
|
d05ad44b84 | ||
|
|
b352c97479 | ||
|
|
063085a6bb | ||
|
|
b4bca16109 | ||
|
|
03770c52fe | ||
|
|
10fe5cdd5d | ||
|
|
57d459b917 | ||
|
|
fe26594f12 | ||
|
|
0d8065fc1f | ||
|
|
a3cdae1e94 | ||
|
|
29d77b649b | ||
|
|
9e190cee81 | ||
|
|
687bfd0f17 | ||
|
|
7d7df4f749 | ||
|
|
958dede319 | ||
|
|
76e814944d | ||
|
|
fbc1d75e9a | ||
|
|
b3c7ce05dc | ||
|
|
feea881e09 | ||
|
|
b978bc4876 | ||
|
|
f84181e7a5 | ||
|
|
313ae0f86c | ||
|
|
a39c018359 | ||
|
|
a09f57d908 | ||
|
|
596828cf78 | ||
|
|
44843aa9cd | ||
|
|
5f930cc4d1 | ||
|
|
7defad5d95 | ||
|
|
707af8a295 | ||
|
|
e0fb9ffbb0 | ||
|
|
d614de6f5e | ||
|
|
86b94b4723 | ||
|
|
5070300050 | ||
|
|
1919546441 | ||
|
|
1c3cd8d44b | ||
|
|
292296266f | ||
|
|
a3e8848bc8 | ||
|
|
ced146fc63 | ||
|
|
6ed79f6a0d | ||
|
|
15e4d86e92 | ||
|
|
5a29a7d2a3 | ||
|
|
034740ce46 | ||
|
|
a2847246e6 | ||
|
|
04479ad660 | ||
|
|
35d2fff593 | ||
|
|
2b93fe9a30 | ||
|
|
5c33a2bd5c | ||
|
|
a28f113105 | ||
|
|
eb7976a2ef | ||
|
|
15db1c0c30 | ||
|
|
2b83c95869 |
30
.github/workflows/master_updater.yml
vendored
30
.github/workflows/master_updater.yml
vendored
@@ -5,33 +5,9 @@ on:
|
||||
types: released
|
||||
|
||||
jobs:
|
||||
updater:
|
||||
User-agent:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SKIP: "0"
|
||||
to_branch: "master"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: desktop-app/action_code_updater@master
|
||||
with:
|
||||
fetch-depth: 0
|
||||
if: env.SKIP == '0'
|
||||
- name: Push the code to the master branch.
|
||||
if: env.SKIP == '0'
|
||||
run: |
|
||||
token=${{ secrets.TOKEN_FOR_MASTER_UPDATER }}
|
||||
if [ -z "${token}" ]; then
|
||||
echo "Token is unset. Nothing to do."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
url=https://x-access-token:$token@github.com/$GITHUB_REPOSITORY
|
||||
latest_tag=$(git describe --tags --abbrev=0)
|
||||
echo "Latest tag: $latest_tag"
|
||||
|
||||
git remote set-url origin $url
|
||||
git remote -v
|
||||
git checkout master
|
||||
git merge $latest_tag
|
||||
|
||||
git push origin HEAD:refs/heads/$to_branch
|
||||
echo "Done!"
|
||||
type: "dev-to-master"
|
||||
|
||||
2
.github/workflows/user_agent_updater.yml
vendored
2
.github/workflows/user_agent_updater.yml
vendored
@@ -6,8 +6,6 @@ on:
|
||||
schedule:
|
||||
# At 00:00 on day-of-month 1.
|
||||
- cron: "0 0 1 * *"
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
User-agent:
|
||||
|
||||
@@ -523,6 +523,8 @@ PRIVATE
|
||||
data/notify/data_notify_settings.h
|
||||
data/notify/data_peer_notify_settings.cpp
|
||||
data/notify/data_peer_notify_settings.h
|
||||
data/notify/data_peer_notify_volume.cpp
|
||||
data/notify/data_peer_notify_volume.h
|
||||
data/stickers/data_custom_emoji.cpp
|
||||
data/stickers/data_custom_emoji.h
|
||||
data/stickers/data_stickers_set.cpp
|
||||
@@ -634,6 +636,8 @@ PRIVATE
|
||||
data/data_report.h
|
||||
data/data_saved_messages.cpp
|
||||
data/data_saved_messages.h
|
||||
data/data_saved_music.cpp
|
||||
data/data_saved_music.h
|
||||
data/data_saved_sublist.cpp
|
||||
data/data_saved_sublist.h
|
||||
data/data_search_controller.cpp
|
||||
@@ -813,6 +817,8 @@ PRIVATE
|
||||
history/view/media/history_view_poll.h
|
||||
history/view/media/history_view_premium_gift.cpp
|
||||
history/view/media/history_view_premium_gift.h
|
||||
history/view/media/history_view_save_document_action.cpp
|
||||
history/view/media/history_view_save_document_action.h
|
||||
history/view/media/history_view_service_box.cpp
|
||||
history/view/media/history_view_service_box.h
|
||||
history/view/media/history_view_similar_channels.cpp
|
||||
@@ -876,6 +882,8 @@ PRIVATE
|
||||
history/view/history_view_fake_items.h
|
||||
history/view/history_view_group_call_bar.cpp
|
||||
history/view/history_view_group_call_bar.h
|
||||
history/view/history_view_group_members_widget.cpp
|
||||
history/view/history_view_group_members_widget.h
|
||||
history/view/history_view_item_preview.h
|
||||
history/view/history_view_list_widget.cpp
|
||||
history/view/history_view_list_widget.h
|
||||
@@ -1046,6 +1054,11 @@ PRIVATE
|
||||
info/reactions_list/info_reactions_list_widget.h
|
||||
info/requests_list/info_requests_list_widget.cpp
|
||||
info/requests_list/info_requests_list_widget.h
|
||||
info/saved/info_saved_music_common.h
|
||||
info/saved/info_saved_music_provider.cpp
|
||||
info/saved/info_saved_music_provider.h
|
||||
info/saved/info_saved_music_widget.cpp
|
||||
info/saved/info_saved_music_widget.h
|
||||
info/saved/info_saved_sublists_widget.cpp
|
||||
info/saved/info_saved_sublists_widget.h
|
||||
info/settings/info_settings_widget.cpp
|
||||
@@ -1119,6 +1132,8 @@ PRIVATE
|
||||
inline_bots/inline_results_widget.h
|
||||
intro/intro_code.cpp
|
||||
intro/intro_code.h
|
||||
intro/intro_email.cpp
|
||||
intro/intro_email.h
|
||||
intro/intro_password_check.cpp
|
||||
intro/intro_password_check.h
|
||||
intro/intro_phone.cpp
|
||||
@@ -1274,6 +1289,8 @@ PRIVATE
|
||||
menu/menu_antispam_validator.h
|
||||
menu/menu_item_download_files.cpp
|
||||
menu/menu_item_download_files.h
|
||||
menu/menu_item_rate_transcribe_session.cpp
|
||||
menu/menu_item_rate_transcribe_session.h
|
||||
menu/menu_mute.cpp
|
||||
menu/menu_mute.h
|
||||
menu/menu_send.cpp
|
||||
@@ -1307,6 +1324,8 @@ PRIVATE
|
||||
mtproto/special_config_request.cpp
|
||||
mtproto/special_config_request.h
|
||||
mtproto/type_utils.h
|
||||
overview/overview_checkbox.cpp
|
||||
overview/overview_checkbox.h
|
||||
overview/overview_layout.cpp
|
||||
overview/overview_layout.h
|
||||
overview/overview_layout_delegate.h
|
||||
@@ -1427,10 +1446,6 @@ PRIVATE
|
||||
platform/platform_window_title.h
|
||||
profile/profile_back_button.cpp
|
||||
profile/profile_back_button.h
|
||||
profile/profile_block_group_members.cpp
|
||||
profile/profile_block_group_members.h
|
||||
profile/profile_block_peer_list.cpp
|
||||
profile/profile_block_peer_list.h
|
||||
profile/profile_block_widget.cpp
|
||||
profile/profile_block_widget.h
|
||||
profile/profile_cover_drop_area.cpp
|
||||
@@ -1625,8 +1640,6 @@ PRIVATE
|
||||
ui/text/format_song_document_name.h
|
||||
ui/widgets/expandable_peer_list.cpp
|
||||
ui/widgets/expandable_peer_list.h
|
||||
ui/widgets/label_with_custom_emoji.cpp
|
||||
ui/widgets/label_with_custom_emoji.h
|
||||
ui/widgets/chat_filters_tabs_strip.cpp
|
||||
ui/widgets/chat_filters_tabs_strip.h
|
||||
ui/widgets/peer_bubble.cpp
|
||||
@@ -1659,6 +1672,8 @@ PRIVATE
|
||||
window/window_adaptive.h
|
||||
window/window_chat_preview.cpp
|
||||
window/window_chat_preview.h
|
||||
window/window_chat_switch_process.cpp
|
||||
window/window_chat_switch_process.h
|
||||
window/window_connecting_widget.cpp
|
||||
window/window_connecting_widget.h
|
||||
window/window_controller.cpp
|
||||
|
||||
BIN
Telegram/Resources/animations/transcribe_loading.tgs
Normal file
BIN
Telegram/Resources/animations/transcribe_loading.tgs
Normal file
Binary file not shown.
7
Telegram/Resources/icons/limits/mini_gift_lock.svg
Normal file
7
Telegram/Resources/icons/limits/mini_gift_lock.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Mini / mini_gift_lock</title>
|
||||
<g id="Mini-/-mini_gift_lock" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M34.6666667,26.3333333 C40.1895142,26.3333333 44.6666667,30.8104858 44.6666667,36.3333333 C44.6666667,41.8561808 40.1895142,46.3333333 34.6666667,46.3333333 C29.1438192,46.3333333 24.6666667,41.8561808 24.6666667,36.3333333 C24.6666667,30.8104858 29.1438192,26.3333333 34.6666667,26.3333333 Z M22,3.06666667 C27.8542183,3.06666667 32.6,7.81244832 32.6,13.6666667 L32.5999548,19.4672746 C34.7232323,19.9235567 36.4292218,21.5034006 37.0650949,23.5539779 C36.287246,23.4090943 35.4858038,23.3333333 34.6666667,23.3333333 C27.4869649,23.3333333 21.6666667,29.1536316 21.6666667,36.3333333 C21.6666667,38.3649522 22.1326999,40.2877232 22.9636948,42.0005746 L12.6666667,42 C9.35295817,42 6.66666667,39.3137085 6.66666667,36 L6.66666667,25.3333333 C6.66666667,22.454169 8.69461762,20.048658 11.4000452,19.4672746 L11.4,13.6666667 C11.4,7.81244832 16.1457817,3.06666667 22,3.06666667 Z M34.6666667,29.3061633 C33.7052821,29.3061633 32.9259259,30.0855195 32.9259259,31.0469041 L32.9259259,36.9308449 C32.9259259,37.7182476 33.2996271,38.4589206 33.9329602,38.9267797 L37.3333333,41.3922119 C38.1066056,41.9634476 39.1965447,41.7996646 39.7677804,41.0263923 L39.8381314,40.9238035 C40.323622,40.1593232 40.1416125,39.1383445 39.4019608,38.5919452 L36.4074074,36.4264726 L36.4074074,31.0469041 C36.4074074,30.0855195 35.6280512,29.3061633 34.6666667,29.3061633 Z M22,7.6 C18.6494725,7.6 15.9333333,10.3161392 15.9333333,13.6666667 L15.9326667,19.3326667 L28.0666667,19.3326667 L28.0666667,13.6666667 C28.0666667,10.3161392 25.3505275,7.6 22,7.6 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -377,6 +377,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_intro_fragment_about" = "Get the code for {phone_number} in the Anonymous Numbers section on Fragment.";
|
||||
"lng_intro_fragment_button" = "Open Fragment";
|
||||
|
||||
"lng_intro_email_setup_title" = "Choose a login email";
|
||||
"lng_intro_email_confirm_subtitle" = "Please check your email {email} (don't forget the spam folder) and enter the code we just sent you.";
|
||||
|
||||
"lng_phone_title" = "Your Phone Number";
|
||||
"lng_phone_desc" = "Please confirm your country code\nand enter your phone number.";
|
||||
"lng_phone_to_qr" = "Quick log in using QR code";
|
||||
@@ -512,6 +515,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_notify_global" = "Global settings";
|
||||
"lng_settings_notify_title" = "Notifications for chats";
|
||||
"lng_settings_desktop_notify" = "Desktop notifications";
|
||||
"lng_settings_master_volume_notifications" = "Volume";
|
||||
"lng_settings_native_title" = "System integration";
|
||||
"lng_settings_use_windows" = "Use Windows notifications";
|
||||
"lng_settings_skip_in_focus" = "Respect system Focus mode";
|
||||
@@ -552,12 +556,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_notification_title_private_chats" = "Notifications for private chats";
|
||||
"lng_notification_about_private_chats#one" = "Please note that **{count} chat** is listed as an exception and won't be affected by this change.";
|
||||
"lng_notification_about_private_chats#other" = "Please note that **{count} chats** are listed as exceptions and won't be affected by this change.";
|
||||
"lng_notification_volume_private_chats" = "Notifications volume for private chats";
|
||||
"lng_notification_title_groups" = "Notifications for groups";
|
||||
"lng_notification_about_groups#one" = "Please note that **{count} group** is listed as an exception and won't be affected by this change.";
|
||||
"lng_notification_about_groups#other" = "Please note that **{count} groups** are listed as exceptions and won't be affected by this change.";
|
||||
"lng_notification_volume_groups" = "Notifications volume for groups";
|
||||
"lng_notification_title_channels" = "Notifications for channels";
|
||||
"lng_notification_about_channels#one" = "Please note that **{count} channel** is listed as an exception and won't be affected by this change.";
|
||||
"lng_notification_about_channels#other" = "Please note that **{count} channels** are listed as exceptions and won't be affected by this change.";
|
||||
"lng_notification_volume_channel" = "Notifications volume for channels";
|
||||
"lng_notification_exceptions_view" = "View exceptions";
|
||||
"lng_notification_enable" = "Enable notifications";
|
||||
"lng_notification_sound" = "Sound";
|
||||
@@ -687,6 +694,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_shortcuts_media_fullscreen" = "Toggle video fullscreen";
|
||||
"lng_shortcuts_show_chat_menu" = "Show chat menu";
|
||||
"lng_shortcuts_show_chat_preview" = "Show chat preview";
|
||||
"lng_shortcuts_record_voice_message" = "Record Voice Message";
|
||||
"lng_shortcuts_record_round_message" = "Record Round Message";
|
||||
|
||||
"lng_settings_chat_reactions_title" = "Quick Reaction";
|
||||
"lng_settings_chat_reactions_subtitle" = "Choose your favorite reaction";
|
||||
@@ -1238,6 +1247,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_quick_dialog_action_toast_archive_success" = "The chat has been archived.";
|
||||
"lng_quick_dialog_action_toast_unarchive_success" = "The chat has been unarchived.";
|
||||
|
||||
"lng_archive_hint_title" = "This is your Archive";
|
||||
"lng_archive_hint_about" = "Archived chats will remain in the Archive when you receive a new message. {link}";
|
||||
"lng_archive_hint_about_unmuted" = "When you receive a new message, muted chats will remain in the Archive, while unmuted chats will be moved to Chats. {link}";
|
||||
"lng_archive_hint_about_link" = "Tap to change {emoji}";
|
||||
"lng_archive_hint_section_1" = "Archived Chats";
|
||||
"lng_archive_hint_section_1_info" = "Move any chat into your Archive and back by swiping on it.";
|
||||
"lng_archive_hint_section_2" = "Hiding Archive";
|
||||
"lng_archive_hint_section_2_info" = "Hide the Archive from your Main screen by swiping on it.";
|
||||
"lng_archive_hint_section_3" = "Stories";
|
||||
"lng_archive_hint_section_3_info" = "Archive Stories from your contacts separately from chats with them.";
|
||||
"lng_archive_hint_button" = "Got it";
|
||||
|
||||
"lng_settings_generic_subscribe" = "Subscribe to {link} to use this setting.";
|
||||
"lng_settings_generic_subscribe_link" = "Telegram Premium";
|
||||
|
||||
@@ -1638,6 +1659,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_media_type_audios" = "Voice messages";
|
||||
"lng_media_type_links" = "Shared links";
|
||||
"lng_media_type_rounds" = "Video messages";
|
||||
"lng_media_saved_music_your" = "Your playlist";
|
||||
"lng_media_saved_music_title" = "Playlist";
|
||||
"lng_profile_common_groups_section" = "Groups in common";
|
||||
"lng_info_edit_contact" = "Edit contact";
|
||||
"lng_info_delete_contact" = "Delete contact";
|
||||
@@ -2055,6 +2078,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_sure_delete_contact" = "Are you sure you want to delete {contact} from your contact list?";
|
||||
"lng_sure_delete_history" = "Are you sure you want to delete all message history with {contact}?\n\nThis action cannot be undone.";
|
||||
"lng_sure_delete_group_history" = "Are you sure you want to delete all messages in \"{group}\"?\n\nThis action cannot be undone.";
|
||||
"lng_sure_delete_channel_history" = "Are you sure you want to delete all messages in \"{channel}\"?\n\n**This action cannot be undone.**";
|
||||
"lng_sure_delete_and_exit" = "Are you sure you want to delete all message history and leave «{group}»?\n\nThis action cannot be undone.";
|
||||
"lng_sure_leave_channel" = "Are you sure you want to leave\nthis channel?";
|
||||
"lng_sure_delete_channel" = "Are you sure you want to delete this channel? All subscribers will be removed and all messages will be lost.";
|
||||
@@ -2167,6 +2191,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_you_proximity_reached" = "You are now within {distance} from {user}";
|
||||
"lng_action_you_theme_changed" = "You changed the chat theme to {emoji}";
|
||||
"lng_action_theme_changed" = "{from} changed the chat theme to {emoji}";
|
||||
"lng_action_you_gift_theme_changed" = "You set {name} as a new theme for this chat.";
|
||||
"lng_action_gift_theme_changed" = "{from} set {name} as a new theme for this chat.";
|
||||
"lng_action_you_theme_disabled" = "You disabled the chat theme";
|
||||
"lng_action_theme_disabled" = "{from} disabled the chat theme";
|
||||
"lng_action_proximity_distance_m#one" = "{count} meter";
|
||||
@@ -2183,6 +2209,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_gift_upgraded_self_channel" = "You turned this gift to {channel} into a unique collectible";
|
||||
"lng_action_gift_upgraded_mine" = "You turned the gift from {user} into a unique collectible";
|
||||
"lng_action_gift_upgraded_self" = "You turned this gift into a unique collectible";
|
||||
"lng_action_gift_sent_upgrade_other" = "{from} sent an upgrade worth {cost} for the gift you received from {user}.";
|
||||
"lng_action_gift_sent_upgrade_self_other" = "You sent an upgrade worth {cost} for the gift {name} received from {user}.";
|
||||
"lng_action_gift_sent_upgrade" = "{from} sent an upgrade worth {cost} for your gift.";
|
||||
"lng_action_gift_sent_upgrade_self" = "You sent an upgrade worth {cost} for this gift.";
|
||||
"lng_action_gift_sent_upgrade_self_channel" = "You sent an upgrade worth {cost} for your gift to {name}.";
|
||||
"lng_action_gift_upgraded_helped" = "{user} unpacked the gift that you helped to upgrade.";
|
||||
"lng_action_gift_upgraded_helped_self" = "You unpacked the gift that {user} helped to upgrade.";
|
||||
"lng_action_gift_transferred" = "{user} transferred you a gift";
|
||||
"lng_action_gift_transferred_channel" = "{user} transferred a gift to {channel}";
|
||||
"lng_action_gift_transferred_unknown" = "Someone transferred you a gift";
|
||||
@@ -2339,6 +2372,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_peer_gifts_filter_by_value" = "Sort by Value";
|
||||
"lng_peer_gifts_filter_by_date" = "Sort by Date";
|
||||
"lng_peer_gifts_filter_unlimited" = "Unlimited";
|
||||
"lng_peer_gifts_filter_upgradable" = "Upgradeable";
|
||||
"lng_peer_gifts_filter_limited" = "Limited";
|
||||
"lng_peer_gifts_filter_unique" = "Unique";
|
||||
"lng_peer_gifts_filter_saved" = "Displayed";
|
||||
@@ -3633,6 +3667,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_unique_status" = "Status";
|
||||
"lng_gift_unique_status_non" = "Non-Unique";
|
||||
"lng_gift_unique_upgrade" = "Upgrade";
|
||||
"lng_gift_unique_upgrade_next" = "Upgrade Next Gift";
|
||||
"lng_gift_unique_gift_upgrade" = "Gift an Upgrade";
|
||||
"lng_gift_unique_gift_pay_upgrade" = "Pay {amount} for Upgrade";
|
||||
"lng_gift_unique_number" = "Collectible #{index}";
|
||||
"lng_gift_unique_number_by" = "Collectible #{index} by {name}";
|
||||
"lng_gift_unique_model" = "Model";
|
||||
@@ -3642,6 +3679,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_unique_availability_label" = "Quantity";
|
||||
"lng_gift_unique_availability#one" = "{count} of {amount} issued";
|
||||
"lng_gift_unique_availability#other" = "{count} of {amount} issued";
|
||||
"lng_gift_unique_value" = "Value";
|
||||
"lng_gift_unique_value_learn_more" = "learn more";
|
||||
"lng_gift_unique_info" = "Gifted to {recipient} on {date}.";
|
||||
"lng_gift_unique_info_sender" = "Gifted by {from} to {recipient} on {date}.";
|
||||
"lng_gift_unique_info_sender_comment" = "Gifted by {from} to {recipient} on {date} with the comment \"{text}\".";
|
||||
@@ -3650,6 +3689,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_availability_left#one" = "{count} of {amount} left";
|
||||
"lng_gift_availability_left#other" = "{count} of {amount} left";
|
||||
"lng_gift_availability_none" = "None of {amount} left";
|
||||
"lng_gift_value_about_average" = "This is the average sale price of {gift} gifts on Telegram and Fragment over the past month.";
|
||||
"lng_gift_value_about_last" = "This is the price at which {gift} was last sold on {platform}.";
|
||||
"lng_gift_value_initial_sale" = "Initial Sale";
|
||||
"lng_gift_value_initial_price" = "Initial Price";
|
||||
"lng_gift_value_initial_price_value" = "{stars} ({amount})";
|
||||
"lng_gift_value_last_sale" = "Last Sale";
|
||||
"lng_gift_value_last_price" = "Last Price";
|
||||
"lng_gift_value_minimum_price" = "Minimum Price";
|
||||
"lng_gift_value_minimum_price_tooltip" = "{amount} is the floor price for {gift} gifts listed on Telegram and Fragment.";
|
||||
"lng_gift_vlaue_average_price" = "Average Price";
|
||||
"lng_gift_value_average_price_tooltip" = "{amount} is the average sale price of {gift} gifts on Telegram and Fragment over the past month.";
|
||||
"lng_gift_value_availability#one" = "{count} {emoji} for sale on {platform} {arrow}";
|
||||
"lng_gift_value_availability#other" = "{count} {emoji} for sale on {platform} {arrow}";
|
||||
"lng_gift_value_telegram" = "Telegram";
|
||||
"lng_gift_value_fragment" = "Fragment";
|
||||
"lng_gift_convert_to_stars#one" = "Convert to {count} Star";
|
||||
"lng_gift_convert_to_stars#other" = "Convert to {count} Stars";
|
||||
"lng_gift_convert_sure_title" = "Convert Gift to Stars";
|
||||
@@ -3685,10 +3739,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_upgrade_preview_about_channel" = "Let the admins of {name} turn your gift into a unique collectible.";
|
||||
"lng_gift_upgrade_unique_title" = "Unique";
|
||||
"lng_gift_upgrade_unique_about" = "Get a unique number, model, backdrop and symbol for your gift.";
|
||||
"lng_gift_upgrade_unique_about_user" = "{name} will get a unique number, model, backdrop and symbol for your gift.";
|
||||
"lng_gift_upgrade_unique_about_channel" = "Admins of {name} will get a unique number, model, backdrop and symbol for your gift.";
|
||||
"lng_gift_upgrade_transferable_title" = "Transferable";
|
||||
"lng_gift_upgrade_transferable_about" = "Send your upgraded gift to any of your friends on Telegram.";
|
||||
"lng_gift_upgrade_transferable_about_user" = "{name} will be able to send the gift to anyone on Telegram.";
|
||||
"lng_gift_upgrade_transferable_about_channel" = "Admins of {name} will be able to send the gift to anyone on Telegram.";
|
||||
"lng_gift_upgrade_tradable_title" = "Tradable";
|
||||
"lng_gift_upgrade_tradable_about" = "Sell or auction your gift on third-party NFT marketplaces.";
|
||||
"lng_gift_upgrade_tradable_about_user" = "{name} will be able to sell the gift on Telegram and NFT marketplaces.";
|
||||
"lng_gift_upgrade_tradable_about_channel" = "Admins of {name} will be able to sell the gift on Telegram and NFT marketplaces.";
|
||||
"lng_gift_upgrade_button" = "Upgrade for {price}";
|
||||
"lng_gift_upgrade_free" = "Upgrade for Free";
|
||||
"lng_gift_upgrade_confirm" = "Confirm";
|
||||
@@ -3698,6 +3758,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_upgrade_add_comment" = "Add sender's name and comment";
|
||||
"lng_gift_upgraded_title" = "Gift Upgraded";
|
||||
"lng_gift_upgraded_about" = "Your gift {name} now has unique attributes and can be transferred to others";
|
||||
"lng_gift_upgrade_gifted_title" = "Upgrade Gifted";
|
||||
"lng_gift_upgrade_gifted_about" = "Now {name} can turn your gift into a unique collectible.";
|
||||
"lng_gift_upgrade_gifted_about_channel" = "Now the admins of {name} can turn your gift into a unique collectible.";
|
||||
"lng_gift_transferred_title" = "Gift Transferred";
|
||||
"lng_gift_transferred_about" = "{name} was successfully transferred to {recipient}.";
|
||||
"lng_gift_transfer_title" = "Transfer {name}";
|
||||
@@ -3724,6 +3787,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_transfer_sure_for" = "Do you want to transfer ownership of {name} to {recipient} for {price}?";
|
||||
"lng_gift_transfer_button" = "Transfer";
|
||||
"lng_gift_transfer_button_for" = "Transfer for {price}";
|
||||
"lng_gift_transfer_set_theme" = "Set as Theme in...";
|
||||
"lng_gift_transfer_choose" = "Choose Chat";
|
||||
"lng_gift_transfer_wear" = "Wear";
|
||||
"lng_gift_transfer_take_off" = "Take Off";
|
||||
"lng_gift_transfer_sell" = "Sell";
|
||||
@@ -3806,6 +3871,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_collection_delete_sure" = "Are you sure you want to delete this collection?";
|
||||
"lng_gift_collection_delete_button" = "Delete";
|
||||
"lng_gift_collection_add_to" = "Add to Collection";
|
||||
"lng_gift_locked_title" = "Gift Locked";
|
||||
|
||||
"lng_accounts_limit_title" = "Limit Reached";
|
||||
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected account.";
|
||||
@@ -4124,6 +4190,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_dialogs_suggestions_credits_sub_low_title#other" = "{emoji} {count} Stars needed for {channels}";
|
||||
"lng_dialogs_suggestions_credits_sub_low_about" = "Insufficient funds to cover your subscription.";
|
||||
|
||||
"lng_unconfirmed_auth_title" = "Someone just got access to your messages!";
|
||||
"lng_unconfirmed_auth_confirm" = "Yes, it’s me";
|
||||
"lng_unconfirmed_auth_deny" = "No, it’s not me!";
|
||||
"lng_unconfirmed_auth_single" = "We detected a new login to your account from {from}, {country}. Is it you?";
|
||||
"lng_unconfirmed_auth_multiple#one" = "We detected new {count} login to your account. Is it you?";
|
||||
"lng_unconfirmed_auth_multiple#other" = "We detected new {count} logins to your account. Is it you?";
|
||||
"lng_unconfirmed_auth_multiple_from#one" = "We detected new {count} login to your account from {country}. Is it you?";
|
||||
"lng_unconfirmed_auth_multiple_from#other" = "We detected new {count} logins to your account from {country}. Is it you?";
|
||||
"lng_unconfirmed_auth_denied_title#one" = "New Login Prevented";
|
||||
"lng_unconfirmed_auth_denied_title#other" = "New Logins Prevented";
|
||||
"lng_unconfirmed_auth_denied_single" = "We have terminated the login attempt from {country}.";
|
||||
"lng_unconfirmed_auth_denied_multiple" = "We have terminated the login attempts from: {country}";
|
||||
"lng_unconfirmed_auth_denied_warning" = "Never send your login code to anyone or you can lose your Telegram account!";
|
||||
"lng_unconfirmed_auth_confirmed" = "New Login Allowed";
|
||||
"lng_unconfirmed_auth_confirmed_message" = "You can check the list of your active logins in {link}.";
|
||||
|
||||
"lng_about_random" = "Send a {emoji} emoji to any chat to try your luck.";
|
||||
"lng_about_random_send" = "Send";
|
||||
|
||||
@@ -4291,6 +4373,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_archive_to_list" = "Move to chat list";
|
||||
"lng_context_archive_to_menu_info" = "Archive moved to the main menu!\nRight click the archive button to return the Archive to your chat list.";
|
||||
"lng_context_archive_settings" = "Archive settings";
|
||||
"lng_context_archive_how_does_it_work" = "How does it work?";
|
||||
|
||||
"lng_context_mute" = "Mute notifications";
|
||||
"lng_context_unmute" = "Unmute";
|
||||
@@ -4302,6 +4385,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_remove_from_group" = "Remove from group";
|
||||
"lng_context_add_to_group" = "Add to group";
|
||||
|
||||
"lng_context_rate_transcription" = "Rate transcription";
|
||||
"lng_toast_sent_rate_transcription" = "Thank you for your feedback!";
|
||||
|
||||
"lng_context_copy_link" = "Copy Link";
|
||||
"lng_context_copy_message_link" = "Copy Message Link";
|
||||
"lng_context_copy_post_link" = "Copy Post Link";
|
||||
@@ -4321,6 +4407,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_pack_info" = "View Sticker Set";
|
||||
"lng_context_pack_add" = "Add Stickers";
|
||||
"lng_context_save_file" = "Save As...";
|
||||
"lng_context_save_music_to" = "Save to...";
|
||||
"lng_context_save_music_profile" = "... Profile";
|
||||
"lng_context_save_music_saved" = "... Saved Messages";
|
||||
"lng_context_save_music_folder" = "... Downloads";
|
||||
"lng_context_save_music_about" = "Choose where you want this audio to be saved.";
|
||||
"lng_context_copy_text" = "Copy Text";
|
||||
"lng_context_open_gif" = "Open GIF";
|
||||
"lng_context_save_gif" = "Add to GIFs";
|
||||
@@ -4735,6 +4826,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
|
||||
"lng_selected_delete_sure#one" = "Do you want to delete {count} message?";
|
||||
"lng_selected_delete_sure#other" = "Do you want to delete {count} messages?";
|
||||
"lng_selected_remove_saved_music" = "Do you want to remove this file from your profile?";
|
||||
"lng_saved_music_added" = "Audio added to your Profile.";
|
||||
"lng_saved_music_removed" = "Audio removed from your Profile.";
|
||||
"lng_delete_photo_sure" = "Do you want to delete this photo?";
|
||||
"lng_delete_for_everyone_hint#one" = "This will delete it for everyone in this chat.";
|
||||
"lng_delete_for_everyone_hint#other" = "This will delete them for everyone in this chat.";
|
||||
@@ -6260,6 +6354,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_chat_theme_change_wallpaper" = "Change Wallpaper";
|
||||
"lng_chat_theme_title" = "Select theme";
|
||||
"lng_chat_theme_cant_voice" = "Sorry, you can't change the chat theme while you have an unsent voice message.";
|
||||
"lng_chat_theme_gift_replace" = "This gift is already your theme in the chat with {name}. Remove it there and use it here instead?";
|
||||
|
||||
"lng_photo_editor_menu_delete" = "Delete";
|
||||
"lng_photo_editor_menu_flip" = "Flip";
|
||||
@@ -6297,7 +6392,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_sponsored_hide_ads" = "Hide";
|
||||
"lng_sponsored_title" = "What are sponsored messages?";
|
||||
"lng_sponsored_info_description1" = "Unlike other apps, Telegram never uses your private data to target ads. Sponsored messages on Telegram are based solely on the topic of the public channels in which they are shown. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored messages.\n\nUnlike other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers a free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
|
||||
"lng_sponsored_info_description1_linked" = "Unlike other apps, Telegram never uses your private data to target ads. {link}\n\nUnlike other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
|
||||
"lng_sponsored_info_description1_link" = "Learn more in the Privacy Policy";
|
||||
"lng_sponsored_info_description1_url" = "https://telegram.org/privacy#5-6-no-ads-based-on-user-data";
|
||||
"lng_sponsored_info_description2" = "Sponsored Messages are currently in test mode. Once they are fully launched and allow Telegram to cover its basic costs, we will start sharing ad revenue with the owners of public channels in which sponsored messages are displayed.\n\nOnline ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate – together.";
|
||||
"lng_sponsored_info_menu" = "Advertiser info";
|
||||
"lng_sponsored_info_submenu" = "Advertiser: {text}";
|
||||
@@ -6334,6 +6431,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_ringtones_box_title" = "Notification Sound";
|
||||
"lng_ringtones_box_cloud_subtitle" = "Choose your tone";
|
||||
"lng_ringtones_box_volume" = "Volume";
|
||||
"lng_ringtones_box_upload_choose" = "Choose a tone";
|
||||
"lng_ringtones_box_upload_button" = "Upload Sound";
|
||||
"lng_ringtones_box_about" = "Right click on any short voice note or MP3 file in chat and select \"Save for Notifications\". It will appear here.";
|
||||
|
||||
@@ -51,8 +51,8 @@ var LocationPicker = {
|
||||
},
|
||||
init: function (params) {
|
||||
mapboxgl.accessToken = params.token;
|
||||
if (params.protocol) {
|
||||
mapboxgl.config.API_URL = params.protocol + '://domain/api.mapbox.com';
|
||||
if (location.hostname != 'desktop-app-resource') {
|
||||
mapboxgl.config.API_URL = location.protocol + '//' + location.host + '/api.mapbox.com';
|
||||
}
|
||||
|
||||
var options = { container: 'map', config: {
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
<file alias="topics_list.tgs">../../animations/edit_peers/topics_list.tgs</file>
|
||||
<file alias="direct_messages.tgs">../../animations/edit_peers/direct_messages.tgs</file>
|
||||
<file alias="no_chats.tgs">../../animations/no_chats.tgs</file>
|
||||
<file alias="transcribe_loading.tgs">../../animations/transcribe_loading.tgs</file>
|
||||
|
||||
<file alias="dice_idle.tgs">../../animations/dice/dice_idle.tgs</file>
|
||||
<file alias="dart_idle.tgs">../../animations/dice/dart_idle.tgs</file>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="6.0.2.0" />
|
||||
Version="6.1.0.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 6,0,2,0
|
||||
PRODUCTVERSION 6,0,2,0
|
||||
FILEVERSION 6,1,0,0
|
||||
PRODUCTVERSION 6,1,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "6.0.2.0"
|
||||
VALUE "FileVersion", "6.1.0.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "6.0.2.0"
|
||||
VALUE "ProductVersion", "6.1.0.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 6,0,2,0
|
||||
PRODUCTVERSION 6,0,2,0
|
||||
FILEVERSION 6,1,0,0
|
||||
PRODUCTVERSION 6,1,0,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", "6.0.2.0"
|
||||
VALUE "FileVersion", "6.1.0.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "6.0.2.0"
|
||||
VALUE "ProductVersion", "6.1.0.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "core/changelogs.h"
|
||||
#include "core/application.h"
|
||||
#include "core/changelogs.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
@@ -83,7 +86,19 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) {
|
||||
} // namespace
|
||||
|
||||
Authorizations::Authorizations(not_null<ApiWrap*> api)
|
||||
: _api(&api->instance()) {
|
||||
: _api(&api->instance())
|
||||
, _autoconfirmPeriod([=] {
|
||||
constexpr auto kFallbackCount = 604800;
|
||||
return api->session().appConfig().get<int>(
|
||||
u"authorization_autoconfirm_period"_q,
|
||||
kFallbackCount);
|
||||
})
|
||||
, _saveUnreviewed([=] {
|
||||
api->session().settings().setUnreviewed(_unreviewed);
|
||||
api->session().saveSettingsDelayed();
|
||||
}) {
|
||||
_unreviewed = api->session().settings().unreviewed();
|
||||
removeExpiredUnreviewed();
|
||||
Core::App().settings().deviceModelChanges(
|
||||
) | rpl::start_with_next([=](const QString &model) {
|
||||
auto changed = false;
|
||||
@@ -119,6 +134,7 @@ void Authorizations::reload() {
|
||||
) | ranges::views::transform([](const MTPAuthorization &auth) {
|
||||
return ParseEntry(auth.data());
|
||||
}) | ranges::to<List>;
|
||||
removeExpiredUnreviewed();
|
||||
refreshCallsDisabledHereFromCloud();
|
||||
_listChanges.fire({});
|
||||
}).fail([=] {
|
||||
@@ -261,4 +277,129 @@ crl::time Authorizations::lastReceivedTime() {
|
||||
return _lastReceived;
|
||||
}
|
||||
|
||||
const std::vector<Data::UnreviewedAuth> &Authorizations::unreviewed() {
|
||||
removeExpiredUnreviewed();
|
||||
return _unreviewed;
|
||||
}
|
||||
|
||||
void Authorizations::removeExpiredUnreviewed() {
|
||||
const auto now = base::unixtime::now();
|
||||
const auto period = _autoconfirmPeriod();
|
||||
|
||||
const auto oldSize = _unreviewed.size();
|
||||
_unreviewed.erase(
|
||||
std::remove_if(_unreviewed.begin(), _unreviewed.end(),
|
||||
[=](const auto &auth) {
|
||||
return (now - auth.date) >= period;
|
||||
}),
|
||||
_unreviewed.end());
|
||||
|
||||
if (_unreviewed.size() != oldSize) {
|
||||
_saveUnreviewed();
|
||||
}
|
||||
}
|
||||
|
||||
void Authorizations::review(const std::vector<uint64> &hashes, bool confirm) {
|
||||
for (const auto hash : hashes) {
|
||||
if (const auto sent = _reviewRequests.take(hash)) {
|
||||
_api.request(*sent).cancel();
|
||||
}
|
||||
}
|
||||
|
||||
const auto checkComplete = [=] {
|
||||
if (_reviewRequests.empty()) {
|
||||
_saveUnreviewed();
|
||||
_unreviewedChanges.fire({});
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto hash : hashes) {
|
||||
const auto removeFromUnreviewed = [=] {
|
||||
_unreviewed.erase(
|
||||
std::remove_if(_unreviewed.begin(), _unreviewed.end(),
|
||||
[hash](const auto &auth) { return auth.hash == hash; }),
|
||||
_unreviewed.end());
|
||||
_reviewRequests.remove(hash);
|
||||
checkComplete();
|
||||
};
|
||||
|
||||
if (confirm) {
|
||||
using Flag = MTPaccount_ChangeAuthorizationSettings::Flag;
|
||||
const auto id = _api.request(MTPaccount_ChangeAuthorizationSettings(
|
||||
MTP_flags(Flag::f_confirmed),
|
||||
MTP_long(hash),
|
||||
MTPBool(), // encrypted_requests_disabled
|
||||
MTPBool() // call_requests_disabled
|
||||
)).done([=] {
|
||||
removeFromUnreviewed();
|
||||
}).fail([=] {
|
||||
removeFromUnreviewed();
|
||||
}).send();
|
||||
_reviewRequests.emplace(hash, id);
|
||||
} else {
|
||||
const auto id = _api.request(MTPaccount_ResetAuthorization(
|
||||
MTP_long(hash)
|
||||
)).done([=](const MTPBool &result) {
|
||||
if (mtpIsTrue(result)) {
|
||||
_list.erase(
|
||||
ranges::remove(_list, hash, &Entry::hash),
|
||||
end(_list));
|
||||
_listChanges.fire({});
|
||||
}
|
||||
removeFromUnreviewed();
|
||||
}).fail([=] {
|
||||
removeFromUnreviewed();
|
||||
}).send();
|
||||
_reviewRequests.emplace(hash, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<> Authorizations::unreviewedChanges() const {
|
||||
return _unreviewedChanges.events();
|
||||
}
|
||||
|
||||
void Authorizations::apply(const MTPUpdate &update) {
|
||||
removeExpiredUnreviewed();
|
||||
update.match([&](const MTPDupdateNewAuthorization &data) {
|
||||
auto unreviewed = Data::UnreviewedAuth{
|
||||
.hash = data.vhash().v,
|
||||
.unconfirmed = data.is_unconfirmed(),
|
||||
.date = data.vdate().value_or_empty(),
|
||||
.device = qs(data.vdevice().value_or_empty()),
|
||||
.location = qs(data.vlocation().value_or_empty())
|
||||
};
|
||||
if (!unreviewed.unconfirmed) {
|
||||
const auto hash = unreviewed.hash;
|
||||
const auto was = _unreviewed.size();
|
||||
_unreviewed.erase(
|
||||
std::remove_if(
|
||||
_unreviewed.begin(),
|
||||
_unreviewed.end(),
|
||||
[hash](const auto &auth) { return auth.hash == hash; }),
|
||||
_unreviewed.end());
|
||||
if (was != _unreviewed.size()) {
|
||||
_saveUnreviewed();
|
||||
_unreviewedChanges.fire({});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &auth : _unreviewed) {
|
||||
if (auth.hash == unreviewed.hash) {
|
||||
auth = std::move(unreviewed);
|
||||
_saveUnreviewed();
|
||||
_unreviewedChanges.fire({});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_unreviewed.push_back(std::move(unreviewed));
|
||||
_saveUnreviewed();
|
||||
_unreviewedChanges.fire({});
|
||||
}, [](auto&&) {
|
||||
Unexpected("Update in Authorizations::apply.");
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "data/data_authorization.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
class ApiWrap;
|
||||
@@ -35,6 +36,8 @@ public:
|
||||
Fn<void(const MTP::Error &error)> &&fail,
|
||||
std::optional<uint64> hash = std::nullopt);
|
||||
|
||||
void apply(const MTPUpdate &update);
|
||||
|
||||
[[nodiscard]] crl::time lastReceivedTime();
|
||||
|
||||
[[nodiscard]] List list() const;
|
||||
@@ -42,6 +45,11 @@ public:
|
||||
[[nodiscard]] int total() const;
|
||||
[[nodiscard]] rpl::producer<int> totalValue() const;
|
||||
|
||||
[[nodiscard]] const std::vector<Data::UnreviewedAuth> &unreviewed();
|
||||
[[nodiscard]] rpl::producer<> unreviewedChanges() const;
|
||||
|
||||
void review(const std::vector<uint64> &hashes, bool confirm);
|
||||
|
||||
void updateTTL(int days);
|
||||
[[nodiscard]] rpl::producer<int> ttlDays() const;
|
||||
|
||||
@@ -57,6 +65,7 @@ public:
|
||||
|
||||
private:
|
||||
void refreshCallsDisabledHereFromCloud();
|
||||
void removeExpiredUnreviewed();
|
||||
|
||||
MTP::Sender _api;
|
||||
mtpRequestId _requestId = 0;
|
||||
@@ -64,10 +73,16 @@ private:
|
||||
List _list;
|
||||
rpl::event_stream<> _listChanges;
|
||||
|
||||
Fn<int()> _autoconfirmPeriod;
|
||||
std::vector<Data::UnreviewedAuth> _unreviewed;
|
||||
rpl::event_stream<> _unreviewedChanges;
|
||||
Fn<void()> _saveUnreviewed;
|
||||
|
||||
mtpRequestId _ttlRequestId = 0;
|
||||
rpl::variable<int> _ttlDays = 0;
|
||||
|
||||
base::flat_map<uint64, mtpRequestId> _toggleCallsDisabledRequests;
|
||||
base::flat_map<uint64, mtpRequestId> _reviewRequests;
|
||||
rpl::variable<bool> _callsDisabledHere;
|
||||
|
||||
crl::time _lastReceived = 0;
|
||||
|
||||
@@ -142,13 +142,12 @@ void ConfirmSubscriptionBox(
|
||||
const auto content = box->verticalLayout();
|
||||
|
||||
Ui::AddSkip(content, st::confirmInvitePhotoTop);
|
||||
const auto userpicWrap = content->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
object_ptr<Ui::RpWidget>(content)));
|
||||
const auto userpic = userpicWrap->entity();
|
||||
const auto userpic = content->add(
|
||||
object_ptr<Ui::RpWidget>(content),
|
||||
style::al_top);
|
||||
const auto photoSize = st::confirmInvitePhotoSize;
|
||||
userpic->resize(Size(photoSize));
|
||||
userpic->setNaturalWidth(photoSize);
|
||||
const auto creditsIconSize = photoSize / 3;
|
||||
const auto creditsIconCallback =
|
||||
Ui::PaintOutlinedColoredCreditsIconCallback(
|
||||
@@ -188,8 +187,8 @@ void ConfirmSubscriptionBox(
|
||||
}
|
||||
auto p = QPainter(userpic);
|
||||
p.drawImage(0, 0, state->frame);
|
||||
}, userpicWrap->lifetime());
|
||||
userpicWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}, userpic->lifetime());
|
||||
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
if (photo) {
|
||||
state->photoMedia = photo->createMediaView();
|
||||
state->photoMedia->wanted(Data::PhotoSize::Small, Data::FileOrigin());
|
||||
@@ -197,7 +196,7 @@ void ConfirmSubscriptionBox(
|
||||
session->downloaderTaskFinished(
|
||||
) | rpl::start_with_next([=] {
|
||||
userpic->update();
|
||||
}, userpicWrap->entity()->lifetime());
|
||||
}, userpic->lifetime());
|
||||
}
|
||||
} else {
|
||||
state->photoEmpty = std::make_unique<Ui::EmptyUserpic>(
|
||||
@@ -219,7 +218,6 @@ void ConfirmSubscriptionBox(
|
||||
box,
|
||||
tr::lng_channel_invite_subscription_title(),
|
||||
st::inviteLinkSubscribeBoxTitle),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
@@ -234,7 +232,6 @@ void ConfirmSubscriptionBox(
|
||||
Ui::Text::Bold),
|
||||
Ui::Text::WithEntities),
|
||||
st::inviteLinkSubscribeBoxAbout),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(
|
||||
@@ -250,7 +247,6 @@ void ConfirmSubscriptionBox(
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
st::inviteLinkSubscribeBoxTerms),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
{
|
||||
|
||||
@@ -863,6 +863,7 @@ std::optional<Data::StarGift> FromTL(
|
||||
.perUserRemains = data.vper_user_remains().value_or_empty(),
|
||||
.firstSaleDate = data.vfirst_sale_date().value_or_empty(),
|
||||
.lastSaleDate = data.vlast_sale_date().value_or_empty(),
|
||||
.lockedUntilDate = data.vlocked_until_date().value_or_empty(),
|
||||
.requirePremium = data.is_require_premium(),
|
||||
.upgradable = data.vupgrade_stars().has_value(),
|
||||
.birthday = data.is_birthday(),
|
||||
@@ -890,13 +891,20 @@ std::optional<Data::StarGift> FromTL(
|
||||
const auto releasedById = data.vreleased_by()
|
||||
? peerFromMTP(*data.vreleased_by())
|
||||
: PeerId();
|
||||
const auto themeUserId = data.vtheme_peer()
|
||||
? peerFromMTP(*data.vtheme_peer())
|
||||
: PeerId();
|
||||
const auto releasedBy = releasedById
|
||||
? session->data().peer(releasedById).get()
|
||||
: nullptr;
|
||||
const auto themeUser = themeUserId
|
||||
? session->data().peer(themeUserId).get()
|
||||
: nullptr;
|
||||
auto result = Data::StarGift{
|
||||
.id = uint64(data.vid().v),
|
||||
.id = data.vid().v,
|
||||
.unique = std::make_shared<Data::UniqueGift>(Data::UniqueGift{
|
||||
.id = data.vid().v,
|
||||
.initialGiftId = data.vgift_id().v,
|
||||
.slug = qs(data.vslug()),
|
||||
.title = qs(data.vtitle()),
|
||||
.ownerAddress = qs(data.vowner_address().value_or_empty()),
|
||||
@@ -905,12 +913,23 @@ std::optional<Data::StarGift> FromTL(
|
||||
? peerFromMTP(*data.vowner_id())
|
||||
: PeerId()),
|
||||
.releasedBy = releasedBy,
|
||||
.themeUser = themeUser,
|
||||
.nanoTonForResale = FindTonForResale(data.vresell_amount()),
|
||||
.starsForResale = FindStarsForResale(data.vresell_amount()),
|
||||
.number = data.vnum().v,
|
||||
.onlyAcceptTon = data.is_resale_ton_only(),
|
||||
.canBeTheme = data.is_theme_available(),
|
||||
.model = *model,
|
||||
.pattern = *pattern,
|
||||
.value = (data.vvalue_amount()
|
||||
? std::make_shared<Data::UniqueGiftValue>(
|
||||
Data::UniqueGiftValue{
|
||||
.currency = qs(
|
||||
data.vvalue_currency().value_or_empty()),
|
||||
.valuePrice = int64(
|
||||
data.vvalue_amount().value_or_empty()),
|
||||
})
|
||||
: nullptr),
|
||||
}),
|
||||
.document = model->document,
|
||||
.releasedBy = releasedBy,
|
||||
@@ -967,6 +986,8 @@ std::optional<Data::SavedStarGift> FromTL(
|
||||
.starsConverted = int64(data.vconvert_stars().value_or_empty()),
|
||||
.starsUpgradedBySender = int64(
|
||||
data.vupgrade_stars().value_or_empty()),
|
||||
.giftPrepayUpgradeHash = qs(
|
||||
data.vprepaid_upgrade_hash().value_or_empty()),
|
||||
.fromId = (data.vfrom_id()
|
||||
? peerFromMTP(*data.vfrom_id())
|
||||
: PeerId()),
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
|
||||
namespace Api {
|
||||
|
||||
@@ -25,6 +26,32 @@ Transcribes::Transcribes(not_null<ApiWrap*> api)
|
||||
, _api(&api->instance()) {
|
||||
}
|
||||
|
||||
bool Transcribes::isRated(not_null<HistoryItem*> item) const {
|
||||
const auto fullId = item->fullId();
|
||||
for (const auto &[transcribeId, id] : _ids) {
|
||||
if (id == fullId) {
|
||||
return _session->settings().isTranscriptionRated(transcribeId);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Transcribes::rate(not_null<HistoryItem*> item, bool isGood) {
|
||||
const auto fullId = item->fullId();
|
||||
for (const auto &[transcribeId, id] : _ids) {
|
||||
if (id == fullId) {
|
||||
_api.request(MTPmessages_RateTranscribedAudio(
|
||||
item->history()->peer->input,
|
||||
MTP_int(item->id),
|
||||
MTP_long(transcribeId),
|
||||
MTP_bool(isGood))).send();
|
||||
_session->settings().markTranscriptionAsRated(transcribeId);
|
||||
_session->saveSettings();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Transcribes::freeFor(not_null<HistoryItem*> item) const {
|
||||
if (const auto channel = item->history()->peer->asMegagroup()) {
|
||||
const auto owner = &channel->owner();
|
||||
|
||||
@@ -37,6 +37,8 @@ public:
|
||||
void apply(const MTPDupdateTranscribedAudio &update);
|
||||
|
||||
[[nodiscard]] bool freeFor(not_null<HistoryItem*> item) const;
|
||||
[[nodiscard]] bool isRated(not_null<HistoryItem*> item) const;
|
||||
void rate(not_null<HistoryItem*> item, bool isGood);
|
||||
|
||||
[[nodiscard]] bool trialsSupport();
|
||||
[[nodiscard]] TimeId trialsRefreshAt();
|
||||
|
||||
@@ -2213,6 +2213,10 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateNewAuthorization: {
|
||||
session().api().authorizations().apply(update);
|
||||
} break;
|
||||
|
||||
case mtpc_updateServiceNotification: {
|
||||
const auto &d = update.c_updateServiceNotification();
|
||||
const auto text = TextWithEntities {
|
||||
|
||||
@@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_saved_music.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_search_controller.h"
|
||||
#include "data/data_session.h"
|
||||
@@ -2458,7 +2459,17 @@ void ApiWrap::refreshFileReference(
|
||||
v::match(origin.data, [&](Data::FileOriginMessage data) {
|
||||
if (const auto item = _session->data().message(data)) {
|
||||
const auto media = item->media();
|
||||
const auto storyId = media ? media->storyId() : FullStoryId();
|
||||
const auto mediaStory = media ? media->storyId() : FullStoryId();
|
||||
const auto storyId = mediaStory
|
||||
? mediaStory
|
||||
: FullStoryId{
|
||||
(IsStoryMsgId(item->id)
|
||||
? item->history()->peer->id
|
||||
: PeerId()),
|
||||
(IsStoryMsgId(item->id)
|
||||
? StoryIdFromMsgId(item->id)
|
||||
: StoryId())
|
||||
};
|
||||
if (storyId) {
|
||||
request(MTPstories_GetStoriesByID(
|
||||
_session->data().peer(storyId.peer)->input,
|
||||
@@ -2469,6 +2480,17 @@ void ApiWrap::refreshFileReference(
|
||||
request(MTPmessages_GetScheduledMessages(
|
||||
item->history()->peer->input,
|
||||
MTP_vector<MTPint>(1, MTP_int(realId))));
|
||||
} else if (item->isSavedMusicItem()) {
|
||||
const auto user = item->history()->peer->asUser();
|
||||
const auto media = item->media();
|
||||
const auto document = media ? media->document() : nullptr;
|
||||
if (user && document) {
|
||||
request(MTPusers_GetSavedMusicByID(
|
||||
user->inputUser,
|
||||
MTP_vector<MTPInputDocument>(1, document->mtpInput())));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
} else if (item->isBusinessShortcut()) {
|
||||
const auto &shortcuts = _session->data().shortcutMessages();
|
||||
const auto realId = shortcuts.lookupId(item);
|
||||
@@ -3340,8 +3362,8 @@ void ApiWrap::finishForwarding(const SendAction &action) {
|
||||
return;
|
||||
}
|
||||
|
||||
forwardMessages(std::move(toForward), action);
|
||||
history->setForwardDraft(topicRootId, monoforumPeerId, {});
|
||||
forwardMessages(std::move(toForward), action);
|
||||
}
|
||||
|
||||
_session->data().sendHistoryChangeNotifications();
|
||||
@@ -3362,6 +3384,22 @@ void ApiWrap::forwardMessages(
|
||||
|
||||
auto &histories = _session->data().histories();
|
||||
|
||||
for (auto i = begin(draft.items); i != end(draft.items);) {
|
||||
const auto item = *i;
|
||||
if (item->isSavedMusicItem()) {
|
||||
SendExistingDocument(MessageToSend(action), item->media()->document());
|
||||
i = draft.items.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if (draft.items.empty()) {
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct SharedCallback {
|
||||
int requestsLeft = 0;
|
||||
FnMut<void()> callback;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Data {
|
||||
struct UpdatedFileReferences;
|
||||
class WallPaper;
|
||||
struct ResolvedForwardDraft;
|
||||
enum class DefaultNotify;
|
||||
enum class DefaultNotify : uint8_t;
|
||||
enum class StickersType : uchar;
|
||||
class Forum;
|
||||
class ForumTopic;
|
||||
|
||||
@@ -13,12 +13,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/update_checker.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_channel_earn.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_premium.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
@@ -158,3 +166,151 @@ QString currentVersionText() {
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
void ArchiveHintBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
bool unarchiveOnNewMessage,
|
||||
Fn<void()> onUnarchive) {
|
||||
box->setNoContentMargin(true);
|
||||
|
||||
const auto content = box->verticalLayout().get();
|
||||
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto &icon = st::dialogsArchiveUserpic;
|
||||
const auto rect = Rect(icon.size() * 2);
|
||||
auto owned = object_ptr<Ui::RpWidget>(content);
|
||||
owned->resize(rect.size());
|
||||
owned->setNaturalWidth(rect.width());
|
||||
const auto widget = box->addRow(std::move(owned), style::al_top);
|
||||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = Painter(widget);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::activeButtonBg);
|
||||
p.drawEllipse(rect);
|
||||
icon.paintInCenter(p, rect);
|
||||
}, widget->lifetime());
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_archive_hint_title(),
|
||||
st::boxTitle),
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto label = box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
(unarchiveOnNewMessage
|
||||
? tr::lng_archive_hint_about_unmuted
|
||||
: tr::lng_archive_hint_about)(
|
||||
lt_link,
|
||||
tr::lng_archive_hint_about_link(
|
||||
lt_emoji,
|
||||
rpl::single(
|
||||
Ui::Text::IconEmoji(&st::textMoreIconEmoji)),
|
||||
Ui::Text::RichLangValue
|
||||
) | rpl::map([](TextWithEntities text) {
|
||||
return Ui::Text::Link(std::move(text), 1);
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
st::channelEarnHistoryRecipientLabel));
|
||||
label->resizeToWidth(box->width()
|
||||
- rect::m::sum::h(st::boxRowPadding));
|
||||
label->setLink(
|
||||
1,
|
||||
std::make_shared<GenericClickHandler>([=](ClickContext context) {
|
||||
if (context.button == Qt::LeftButton) {
|
||||
onUnarchive();
|
||||
}
|
||||
}));
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto padding = QMargins(
|
||||
st::settingsButton.padding.left(),
|
||||
st::boxRowPadding.top(),
|
||||
st::boxRowPadding.right(),
|
||||
st::boxRowPadding.bottom());
|
||||
const auto addEntry = [&](
|
||||
rpl::producer<QString> title,
|
||||
rpl::producer<QString> about,
|
||||
const style::icon &icon) {
|
||||
const auto top = content->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
std::move(title),
|
||||
st::channelEarnSemiboldLabel),
|
||||
padding);
|
||||
Ui::AddSkip(content, st::channelEarnHistoryThreeSkip);
|
||||
content->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
std::move(about),
|
||||
st::channelEarnHistoryRecipientLabel),
|
||||
padding);
|
||||
const auto left = Ui::CreateChild<Ui::RpWidget>(
|
||||
box->verticalLayout().get());
|
||||
left->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = Painter(left);
|
||||
icon.paint(p, 0, 0, left->width());
|
||||
}, left->lifetime());
|
||||
left->resize(icon.size());
|
||||
top->geometryValue(
|
||||
) | rpl::start_with_next([=](const QRect &g) {
|
||||
left->moveToLeft(
|
||||
(g.left() - left->width()) / 2,
|
||||
g.top() + st::channelEarnHistoryThreeSkip);
|
||||
}, left->lifetime());
|
||||
};
|
||||
addEntry(
|
||||
tr::lng_archive_hint_section_1(),
|
||||
tr::lng_archive_hint_section_1_info(),
|
||||
st::menuIconArchive);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
addEntry(
|
||||
tr::lng_archive_hint_section_2(),
|
||||
tr::lng_archive_hint_section_2_info(),
|
||||
st::menuIconStealth);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
addEntry(
|
||||
tr::lng_archive_hint_section_3(),
|
||||
tr::lng_archive_hint_section_3_info(),
|
||||
st::menuIconStoriesSavedSection);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto &st = st::premiumPreviewDoubledLimitsBox;
|
||||
box->setStyle(st);
|
||||
auto button = object_ptr<Ui::RoundButton>(
|
||||
box,
|
||||
tr::lng_archive_hint_button(),
|
||||
st::defaultActiveButton);
|
||||
button->setTextTransform(
|
||||
Ui::RoundButton::TextTransform::NoTransform);
|
||||
button->resizeToWidth(box->width()
|
||||
- st.buttonPadding.left()
|
||||
- st.buttonPadding.left());
|
||||
button->setClickedCallback([=] { box->closeBox(); });
|
||||
box->addButton(std::move(button));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/layers/generic_box.h"
|
||||
|
||||
void AboutBox(not_null<Ui::GenericBox*> box);
|
||||
void ArchiveHintBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
bool unarchiveOnNewMessage,
|
||||
Fn<void()> onUnarchive);
|
||||
|
||||
QString telegramFaqLink();
|
||||
QString currentVersionText();
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/file_utilities.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -18,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
constexpr auto kUrl = "https://promote.telegram.org"_cs;
|
||||
constexpr auto kUrl = "https://ads.telegram.org"_cs;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -54,8 +55,16 @@ void AboutSponsoredBox(not_null<Ui::GenericBox*> box) {
|
||||
};
|
||||
|
||||
const auto &stLabel = st::aboutLabel;
|
||||
const auto info1 = box->addRow(object_ptr<FlatLabel>(box, stLabel));
|
||||
info1->setText(tr::lng_sponsored_info_description1(tr::now));
|
||||
auto text1 = tr::lng_sponsored_info_description1_linked(
|
||||
lt_link,
|
||||
rpl::combine(
|
||||
tr::lng_sponsored_info_description1_link(),
|
||||
tr::lng_sponsored_info_description1_url()
|
||||
) | rpl::map([](const QString &text, const QString &url) {
|
||||
return Ui::Text::Link(text, url);
|
||||
}),
|
||||
Ui::Text::RichLangValue);
|
||||
box->addRow(object_ptr<FlatLabel>(box, std::move(text1), stLabel));
|
||||
|
||||
box->addSkip(st::sponsoredUrlButtonSkip);
|
||||
addUrl();
|
||||
|
||||
@@ -451,12 +451,12 @@ auto BackgroundBox::Inner::resolveResetCustomPaper() const
|
||||
return {};
|
||||
}
|
||||
const auto nonCustom = Window::Theme::Background()->paper();
|
||||
const auto themeEmoji = _forPeer->themeEmoji();
|
||||
if (forChannel() || themeEmoji.isEmpty()) {
|
||||
const auto themeToken = _forPeer->themeToken();
|
||||
if (forChannel() || themeToken.isEmpty()) {
|
||||
return nonCustom;
|
||||
}
|
||||
const auto &themes = _forPeer->owner().cloudThemes();
|
||||
const auto theme = themes.themeForEmoji(themeEmoji);
|
||||
const auto theme = themes.themeForToken(themeToken);
|
||||
if (!theme) {
|
||||
return nonCustom;
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ constexpr auto kMaxWallPaperSlugLength = 255;
|
||||
return paper;
|
||||
}
|
||||
const auto &themes = session->data().cloudThemes();
|
||||
if (const auto theme = themes.themeForEmoji(paper.emojiId())) {
|
||||
if (const auto theme = themes.themeForToken(paper.emojiId())) {
|
||||
using Type = Data::CloudThemeType;
|
||||
const auto type = dark ? Type::Dark : Type::Light;
|
||||
const auto i = theme->settings.find(type);
|
||||
|
||||
@@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -111,16 +112,12 @@ void DeleteMessagesBox::prepare() {
|
||||
Ui::Text::RichLangValue);
|
||||
deleteStyle = &st::attentionBoxButton;
|
||||
} else if (_wipeHistoryJustClear) {
|
||||
const auto isChannel = peer->isBroadcast();
|
||||
const auto isPublicGroup = peer->isMegagroup()
|
||||
&& peer->asChannel()->isPublic();
|
||||
if (isChannel || isPublicGroup) {
|
||||
canDelete = false;
|
||||
}
|
||||
details.text = isChannel
|
||||
? tr::lng_no_clear_history_channel(tr::now)
|
||||
: isPublicGroup
|
||||
? tr::lng_no_clear_history_group(tr::now)
|
||||
_revokeJustClearForChannel = true;
|
||||
details.text = (peer->isChannel() && !peer->isMegagroup())
|
||||
? tr::lng_sure_delete_channel_history(
|
||||
tr::now,
|
||||
lt_channel,
|
||||
peer->name())
|
||||
: peer->isSelf()
|
||||
? tr::lng_sure_delete_saved_messages(tr::now)
|
||||
: peer->isUser()
|
||||
@@ -156,7 +153,8 @@ void DeleteMessagesBox::prepare() {
|
||||
}
|
||||
deleteStyle = &st::attentionBoxButton;
|
||||
}
|
||||
if (auto revoke = revokeText(peer)) {
|
||||
if (_revokeJustClearForChannel) {
|
||||
} else if (auto revoke = revokeText(peer)) {
|
||||
_revoke.create(
|
||||
this,
|
||||
revoke->checkbox,
|
||||
@@ -226,12 +224,14 @@ void DeleteMessagesBox::prepare() {
|
||||
search->searchMessages({ .from = _moderateFrom });
|
||||
}
|
||||
} else {
|
||||
details.text = (_ids.size() == 1)
|
||||
details.text = hasSavedMusicMessages()
|
||||
? tr::lng_selected_remove_saved_music(tr::now)
|
||||
: (_ids.size() == 1)
|
||||
? tr::lng_selected_delete_sure_this(tr::now)
|
||||
: tr::lng_selected_delete_sure(tr::now, lt_count, _ids.size());
|
||||
if (const auto peer = checkFromSinglePeer()) {
|
||||
auto count = int(_ids.size());
|
||||
if (hasScheduledMessages()) {
|
||||
if (hasScheduledMessages() || hasSavedMusicMessages()) {
|
||||
} else if (auto revoke = revokeText(peer)) {
|
||||
const auto &settings = Core::App().settings();
|
||||
const auto revokeByDefault
|
||||
@@ -285,6 +285,7 @@ void DeleteMessagesBox::prepare() {
|
||||
}
|
||||
}
|
||||
_text.create(this, rpl::single(std::move(details)), st::boxLabel);
|
||||
_text->resizeToWidth(st::boxWidth - rect::m::sum::h(st::boxPadding));
|
||||
|
||||
if (_wipeHistoryJustClear && _wipeHistoryPeer) {
|
||||
const auto validator = TTLMenu::TTLValidator(
|
||||
@@ -314,28 +315,36 @@ void DeleteMessagesBox::prepare() {
|
||||
addButton(tr::lng_about_done(), [=] { closeBox(); });
|
||||
}
|
||||
|
||||
auto fullHeight = st::boxPadding.top()
|
||||
+ _text->height()
|
||||
+ st::boxPadding.bottom();
|
||||
if (_moderateFrom) {
|
||||
fullHeight += st::boxMediumSkip;
|
||||
if (_banUser) {
|
||||
fullHeight += _banUser->heightNoMargins() + st::boxLittleSkip;
|
||||
const auto &padding = st::boxPadding;
|
||||
rpl::combine(
|
||||
widthValue(),
|
||||
_text->naturalWidthValue()
|
||||
) | rpl::start_with_next([=](int full, int) {
|
||||
_text->resizeToNaturalWidth(full - padding.left() - padding.right());
|
||||
|
||||
auto fullHeight = st::boxPadding.top()
|
||||
+ _text->height()
|
||||
+ st::boxPadding.bottom();
|
||||
if (_moderateFrom) {
|
||||
fullHeight += st::boxMediumSkip;
|
||||
if (_banUser) {
|
||||
fullHeight += _banUser->heightNoMargins() + st::boxLittleSkip;
|
||||
}
|
||||
fullHeight += _reportSpam->heightNoMargins();
|
||||
if (_deleteAll) {
|
||||
fullHeight += st::boxLittleSkip + _deleteAll->heightNoMargins();
|
||||
}
|
||||
} else if (_revoke) {
|
||||
fullHeight += st::boxMediumSkip + _revoke->heightNoMargins();
|
||||
}
|
||||
fullHeight += _reportSpam->heightNoMargins();
|
||||
if (_deleteAll) {
|
||||
fullHeight += st::boxLittleSkip + _deleteAll->heightNoMargins();
|
||||
if (_autoDeleteSettings) {
|
||||
fullHeight += st::boxMediumSkip
|
||||
+ _autoDeleteSettings->height()
|
||||
+ st::boxLittleSkip;
|
||||
}
|
||||
} else if (_revoke) {
|
||||
fullHeight += st::boxMediumSkip + _revoke->heightNoMargins();
|
||||
}
|
||||
if (_autoDeleteSettings) {
|
||||
fullHeight += st::boxMediumSkip
|
||||
+ _autoDeleteSettings->height()
|
||||
+ st::boxLittleSkip;
|
||||
}
|
||||
setDimensions(st::boxWidth, fullHeight);
|
||||
_fullHeight = fullHeight;
|
||||
setDimensions(st::boxWidth, fullHeight);
|
||||
_fullHeight = fullHeight;
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
bool DeleteMessagesBox::hasScheduledMessages() const {
|
||||
@@ -349,6 +358,17 @@ bool DeleteMessagesBox::hasScheduledMessages() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeleteMessagesBox::hasSavedMusicMessages() const {
|
||||
for (const auto &fullId : _ids) {
|
||||
if (const auto item = _session->data().message(fullId)) {
|
||||
if (item->isSavedMusicItem()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PeerData *DeleteMessagesBox::checkFromSinglePeer() const {
|
||||
auto result = (PeerData*)nullptr;
|
||||
for (const auto &fullId : _ids) {
|
||||
@@ -554,12 +574,17 @@ void DeleteMessagesBox::deleteAndClear() {
|
||||
!_revoke->checked());
|
||||
Core::App().saveSettingsDelayed();
|
||||
}
|
||||
const auto revoke = _revoke ? _revoke->checked() : _revokeForBot;
|
||||
const auto revoke = _revoke
|
||||
? _revoke->checked()
|
||||
: (_revokeForBot || _revokeJustClearForChannel);
|
||||
const auto session = _session;
|
||||
const auto invokeCallbackAndClose = [&] {
|
||||
// deleteMessages can initiate closing of the current section,
|
||||
// which will cause this box to be destroyed.
|
||||
const auto weak = base::make_weak(this);
|
||||
if (hasSavedMusicMessages()) {
|
||||
uiShow()->showToast(tr::lng_saved_music_removed(tr::now));
|
||||
}
|
||||
if (const auto callback = _deleteConfirmedCallback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ private:
|
||||
void deleteAndClear();
|
||||
[[nodiscard]] PeerData *checkFromSinglePeer() const;
|
||||
[[nodiscard]] bool hasScheduledMessages() const;
|
||||
[[nodiscard]] bool hasSavedMusicMessages() const;
|
||||
[[nodiscard]] std::optional<RevokeConfig> revokeText(
|
||||
not_null<PeerData*> peer) const;
|
||||
[[nodiscard]] PaidPostType paidPostType() const;
|
||||
@@ -75,6 +76,7 @@ private:
|
||||
bool _moderateDeleteAll = false;
|
||||
|
||||
bool _revokeForBot = false;
|
||||
bool _revokeJustClearForChannel = false;
|
||||
|
||||
object_ptr<Ui::FlatLabel> _text = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _revoke = { nullptr };
|
||||
|
||||
@@ -1293,7 +1293,7 @@ void EditDirectMessagesPriceBox(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
box,
|
||||
object_ptr<Ui::VerticalLayout>(box)),
|
||||
{});
|
||||
style::margins());
|
||||
wrap->toggle(savedValue.has_value(), anim::type::instant);
|
||||
wrap->toggleOn(toggle->toggledChanges());
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/label_with_custom_emoji.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_channel_earn.h"
|
||||
@@ -53,9 +52,8 @@ void GiftCreditsBox(
|
||||
Ui::AddSkip(content);
|
||||
const auto &stUser = st::premiumGiftsUserpicButton;
|
||||
const auto userpicWrap = content->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
object_ptr<Ui::UserpicButton>(content, peer, stUser)));
|
||||
object_ptr<Ui::UserpicButton>(content, peer, stUser),
|
||||
style::al_top);
|
||||
userpicWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
@@ -78,7 +76,7 @@ void GiftCreditsBox(
|
||||
u"internal:stars_examples"_q);
|
||||
});
|
||||
content->add(
|
||||
Ui::CreateLabelWithCustomEmoji(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_credits_box_history_entry_gift_out_about(
|
||||
lt_user,
|
||||
@@ -86,7 +84,6 @@ void GiftCreditsBox(
|
||||
lt_link,
|
||||
std::move(link),
|
||||
Ui::Text::RichLangValue),
|
||||
Core::TextContext({ .session = &peer->session() }),
|
||||
st::creditsBoxAbout),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
@@ -100,6 +97,7 @@ void GiftCreditsBox(
|
||||
peer,
|
||||
CreditsAmount(),
|
||||
[=] { gifted(); box->uiShow()->hideLayer(); },
|
||||
box->showFinishes(),
|
||||
tr::lng_credits_summary_options_subtitle(),
|
||||
{});
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_user.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "info/channel_statistics/boosts/giveaway/boost_badge.h" // InfiniteRadialAnimationWidget.
|
||||
#include "info/channel_statistics/earn/earn_icons.h"
|
||||
#include "info/profile/info_profile_badge.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "lang/lang_keys.h"
|
||||
@@ -54,6 +55,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/rect.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/text/custom_emoji_helper.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
@@ -75,9 +78,74 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kRarityTooltipDuration = 3 * crl::time(1000);
|
||||
constexpr auto kTooltipDuration = 3 * crl::time(1000);
|
||||
constexpr auto kPriceTooltipDuration = 6 * crl::time(1000);
|
||||
constexpr auto kHorizontalBar = QChar(0x2015);
|
||||
|
||||
struct InfoTooltipData {
|
||||
not_null<Ui::RpWidget*> parent;
|
||||
Ui::ImportantTooltip *raw = nullptr;
|
||||
};
|
||||
|
||||
void ShowInfoTooltip(
|
||||
std::shared_ptr<InfoTooltipData> data,
|
||||
not_null<QWidget*> target,
|
||||
rpl::producer<TextWithEntities> text,
|
||||
int duration) {
|
||||
if (data->raw) {
|
||||
data->raw->toggleAnimated(false);
|
||||
}
|
||||
const auto parent = data->parent;
|
||||
const auto tooltip = Ui::CreateChild<Ui::ImportantTooltip>(
|
||||
parent,
|
||||
Ui::MakeNiceTooltipLabel(
|
||||
parent,
|
||||
std::move(text),
|
||||
st::boxWideWidth,
|
||||
st::defaultImportantTooltipLabel),
|
||||
st::defaultImportantTooltip);
|
||||
tooltip->toggleFast(false);
|
||||
|
||||
const auto update = [=] {
|
||||
const auto geometry = Ui::MapFrom(parent, target, target->rect());
|
||||
const auto countPosition = [=](QSize size) {
|
||||
const auto left = geometry.x()
|
||||
+ (geometry.width() - size.width()) / 2;
|
||||
const auto right = parent->width()
|
||||
- st::normalFont->spacew;
|
||||
return QPoint(
|
||||
std::max(std::min(left, right - size.width()), 0),
|
||||
geometry.y() - size.height() - st::normalFont->descent);
|
||||
};
|
||||
tooltip->pointAt(geometry, RectPart::Top, countPosition);
|
||||
};
|
||||
parent->widthValue(
|
||||
) | rpl::start_with_next(update, tooltip->lifetime());
|
||||
|
||||
update();
|
||||
tooltip->toggleAnimated(true);
|
||||
|
||||
data->raw = tooltip;
|
||||
tooltip->shownValue() | rpl::filter(
|
||||
!rpl::mappers::_1
|
||||
) | rpl::start_with_next([=] {
|
||||
crl::on_main(tooltip, [=] {
|
||||
if (tooltip->isHidden()) {
|
||||
if (data->raw == tooltip) {
|
||||
data->raw = nullptr;
|
||||
}
|
||||
delete tooltip;
|
||||
}
|
||||
});
|
||||
}, tooltip->lifetime());
|
||||
|
||||
base::timer_once(
|
||||
duration
|
||||
) | rpl::start_with_next([=] {
|
||||
tooltip->toggleAnimated(false);
|
||||
}, tooltip->lifetime());
|
||||
}
|
||||
|
||||
[[nodiscard]] QString CreateMessageLink(
|
||||
not_null<Main::Session*> session,
|
||||
PeerId peerId,
|
||||
@@ -110,6 +178,33 @@ constexpr auto kHorizontalBar = QChar(0x2015);
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] TextWithEntities FormatValuePrice(
|
||||
int64 price,
|
||||
QString currency,
|
||||
bool approximately = false) {
|
||||
auto result = TextWithEntities();
|
||||
if (approximately) {
|
||||
result.append('~');
|
||||
}
|
||||
return result.append(Ui::FillAmountAndCurrency(price, currency));
|
||||
}
|
||||
|
||||
[[nodiscard]] TextWithEntities FormatValueDate(TimeId date) {
|
||||
const auto parsed = base::unixtime::parse(date).date();
|
||||
const auto day = parsed.day();
|
||||
const auto month = parsed.month();
|
||||
const auto year = parsed.year();
|
||||
return { tr::lng_month_day_year(
|
||||
tr::now,
|
||||
lt_month,
|
||||
Lang::MonthDay(month)(tr::now),
|
||||
lt_day,
|
||||
QString::number(day),
|
||||
lt_year,
|
||||
QString::number(year))
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakeLinkCopyIcon(
|
||||
not_null<QWidget*> parent) {
|
||||
auto result = object_ptr<Ui::RpWidget>(parent);
|
||||
@@ -156,6 +251,61 @@ constexpr auto kHorizontalBar = QChar(0x2015);
|
||||
: st::giveawayGiftCodeValueMultiline));
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakeValueWithSmallButton(
|
||||
not_null<Ui::TableLayout*> table,
|
||||
not_null<Ui::RpWidget*> value,
|
||||
rpl::producer<QString> buttonText,
|
||||
Fn<void(not_null<Ui::RpWidget*> button)> handler = nullptr,
|
||||
int topSkip = 0) {
|
||||
class MarginedWidget final : public Ui::RpWidget {
|
||||
public:
|
||||
using RpWidget::RpWidget;
|
||||
QMargins getMargins() const override {
|
||||
return { 0, 0, 0, st::giveawayGiftCodePeerMargin.bottom() };
|
||||
}
|
||||
};
|
||||
auto result = object_ptr<MarginedWidget>(table);
|
||||
const auto raw = result.data();
|
||||
|
||||
value->setParent(raw);
|
||||
value->show();
|
||||
|
||||
const auto button = Ui::CreateChild<Ui::RoundButton>(
|
||||
raw,
|
||||
std::move(buttonText),
|
||||
table->st().smallButton);
|
||||
button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
||||
if (handler) {
|
||||
button->setClickedCallback([button, handler = std::move(handler)] {
|
||||
handler(button);
|
||||
});
|
||||
} else {
|
||||
button->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}
|
||||
rpl::combine(
|
||||
raw->widthValue(),
|
||||
button->widthValue(),
|
||||
value->naturalWidthValue()
|
||||
) | rpl::start_with_next([=](int width, int buttonWidth, int) {
|
||||
const auto buttonSkip = st::normalFont->spacew + buttonWidth;
|
||||
value->resizeToNaturalWidth(width - buttonSkip);
|
||||
value->moveToLeft(0, 0, width);
|
||||
button->moveToLeft(
|
||||
rect::right(value) + st::normalFont->spacew,
|
||||
(topSkip
|
||||
+ (table->st().defaultValue.style.font->ascent
|
||||
- table->st().smallButton.style.font->ascent)),
|
||||
width);
|
||||
}, value->lifetime());
|
||||
|
||||
value->heightValue() | rpl::start_with_next([=](int height) {
|
||||
const auto bottom = st::giveawayGiftCodePeerMargin.bottom();
|
||||
raw->resize(raw->width(), height + bottom);
|
||||
}, raw->lifetime());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakePeerTableValue(
|
||||
not_null<Ui::TableLayout*> table,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
@@ -174,38 +324,18 @@ constexpr auto kHorizontalBar = QChar(0x2015);
|
||||
raw,
|
||||
(button && handler) ? peer->shortName() : peer->name(),
|
||||
table->st().defaultValue);
|
||||
const auto send = (button && handler)
|
||||
? Ui::CreateChild<Ui::RoundButton>(
|
||||
raw,
|
||||
std::move(button),
|
||||
table->st().smallButton)
|
||||
: nullptr;
|
||||
if (send) {
|
||||
send->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
||||
send->setClickedCallback(std::move(handler));
|
||||
}
|
||||
rpl::combine(
|
||||
raw->widthValue(),
|
||||
send ? send->widthValue() : rpl::single(0)
|
||||
) | rpl::start_with_next([=](int width, int sendWidth) {
|
||||
|
||||
raw->widthValue() | rpl::start_with_next([=](int width) {
|
||||
const auto position = st::giveawayGiftCodeNamePosition;
|
||||
const auto sendSkip = sendWidth
|
||||
? (st::normalFont->spacew + sendWidth)
|
||||
: 0;
|
||||
label->resizeToNaturalWidth(width - position.x() - sendSkip);
|
||||
label->resizeToNaturalWidth(width - position.x());
|
||||
label->moveToLeft(position.x(), position.y(), width);
|
||||
const auto top = (raw->height() - userpic->height()) / 2;
|
||||
userpic->moveToLeft(0, top, width);
|
||||
if (send) {
|
||||
send->moveToLeft(
|
||||
position.x() + label->width() + st::normalFont->spacew,
|
||||
(position.y()
|
||||
+ table->st().defaultValue.style.font->ascent
|
||||
- table->st().smallButton.style.font->ascent),
|
||||
width);
|
||||
}
|
||||
}, label->lifetime());
|
||||
|
||||
label->naturalWidthValue() | rpl::start_with_next([=](int width) {
|
||||
raw->setNaturalWidth(st::giveawayGiftCodeNamePosition.x() + width);
|
||||
}, label->lifetime());
|
||||
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
label->setTextColorOverride(table->st().defaultValue.palette.linkFg->c);
|
||||
@@ -214,7 +344,15 @@ constexpr auto kHorizontalBar = QChar(0x2015);
|
||||
show->showBox(PrepareShortInfoBox(peer, show));
|
||||
});
|
||||
|
||||
return result;
|
||||
if (!button || !handler) {
|
||||
return result;
|
||||
}
|
||||
return MakeValueWithSmallButton(
|
||||
table,
|
||||
result.release(),
|
||||
std::move(button),
|
||||
[=](not_null<Ui::RpWidget*> button) { handler(); },
|
||||
st::giveawayGiftCodeNamePosition.y());
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakePeerWithStatusValue(
|
||||
@@ -222,24 +360,21 @@ constexpr auto kHorizontalBar = QChar(0x2015);
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
PeerId id,
|
||||
Fn<void(not_null<Ui::RpWidget*>, EmojiStatusId)> pushStatusId) {
|
||||
auto result = object_ptr<Ui::AbstractButton>(table);
|
||||
auto result = object_ptr<Ui::RpWidget>(table);
|
||||
const auto raw = result.data();
|
||||
|
||||
const auto &st = st::giveawayGiftCodeUserpic;
|
||||
raw->resize(raw->width(), st.photoSize);
|
||||
const auto peerLabel = MakePeerTableValue(table, show, id).release();
|
||||
peerLabel->setParent(raw);
|
||||
peerLabel->show();
|
||||
|
||||
const auto peer = show->session().data().peer(id);
|
||||
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(raw, peer, st);
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
raw,
|
||||
peer->name(),
|
||||
table->st().defaultValue);
|
||||
raw->resize(raw->width(), peerLabel->height());
|
||||
|
||||
using namespace Info::Profile;
|
||||
struct State {
|
||||
rpl::variable<Badge::Content> content;
|
||||
};
|
||||
const auto state = label->lifetime().make_state<State>();
|
||||
const auto peer = show->session().data().peer(id);
|
||||
const auto state = peerLabel->lifetime().make_state<State>();
|
||||
state->content = EmojiStatusIdValue(
|
||||
peer
|
||||
) | rpl::map([=](EmojiStatusId emojiStatusId) {
|
||||
@@ -252,7 +387,7 @@ constexpr auto kHorizontalBar = QChar(0x2015);
|
||||
.emojiStatusId = emojiStatusId,
|
||||
};
|
||||
});
|
||||
const auto badge = label->lifetime().make_state<Badge>(
|
||||
const auto badge = peerLabel->lifetime().make_state<Badge>(
|
||||
raw,
|
||||
st::infoPeerBadge,
|
||||
&peer->session(),
|
||||
@@ -270,32 +405,19 @@ constexpr auto kHorizontalBar = QChar(0x2015);
|
||||
raw->widthValue(),
|
||||
rpl::single(rpl::empty) | rpl::then(badge->updated())
|
||||
) | rpl::start_with_next([=](int width, const auto &) {
|
||||
const auto position = st::giveawayGiftCodeNamePosition;
|
||||
const auto badgeWidget = badge->widget();
|
||||
const auto badgeSkip = badgeWidget
|
||||
? (st::normalFont->spacew + badgeWidget->width())
|
||||
: 0;
|
||||
label->resizeToNaturalWidth(width - position.x() - badgeSkip);
|
||||
label->moveToLeft(position.x(), position.y(), width);
|
||||
const auto top = (raw->height() - userpic->height()) / 2;
|
||||
userpic->moveToLeft(0, top, width);
|
||||
peerLabel->resizeToNaturalWidth(width - badgeSkip);
|
||||
peerLabel->moveToLeft(0, 0, width);
|
||||
if (badgeWidget) {
|
||||
badgeWidget->moveToLeft(
|
||||
position.x() + label->width() + st::normalFont->spacew,
|
||||
(position.y()
|
||||
+ table->st().defaultValue.style.font->ascent
|
||||
- table->st().smallButton.style.font->ascent),
|
||||
peerLabel->width() + st::normalFont->spacew,
|
||||
st::giftBoxByStarsStarTop,
|
||||
width);
|
||||
}
|
||||
}, label->lifetime());
|
||||
|
||||
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
label->setTextColorOverride(table->st().defaultValue.palette.linkFg->c);
|
||||
|
||||
raw->setClickedCallback([=] {
|
||||
show->showBox(PrepareShortInfoBox(peer, show));
|
||||
});
|
||||
}, raw->lifetime());
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -340,7 +462,7 @@ void AddTableRow(
|
||||
not_null<Ui::TableLayout*> table,
|
||||
rpl::producer<QString> label,
|
||||
object_ptr<Ui::RpWidget> value,
|
||||
style::margins valueMargins) {
|
||||
style::margins valueMargins = st::giveawayGiftCodeValueMargin) {
|
||||
table->addRow(
|
||||
(label
|
||||
? object_ptr<Ui::FlatLabel>(
|
||||
@@ -353,55 +475,108 @@ void AddTableRow(
|
||||
valueMargins);
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakePriceWithChangePercentValue(
|
||||
not_null<Ui::TableLayout*> table,
|
||||
const std::shared_ptr<Data::UniqueGiftValue> &value) {
|
||||
auto label = object_ptr<Ui::FlatLabel>(
|
||||
table,
|
||||
rpl::single(FormatValuePrice(value->lastSalePrice, value->currency)),
|
||||
table->st().defaultValue);
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
const auto initial = value->initialSalePrice;
|
||||
if (!initial) {
|
||||
return label;
|
||||
}
|
||||
|
||||
const auto diff = (100 * (value->lastSalePrice - initial))
|
||||
/ float64(initial);
|
||||
const auto use = (std::abs(diff) >= 10.)
|
||||
? base::SafeRound(diff)
|
||||
: (int(base::SafeRound(diff * 100)) / 100.);
|
||||
const auto prefix = (use > 0) ? u"+"_q : QString();
|
||||
const auto percent = Lang::FormatExactCountDecimal(use) + '%';
|
||||
auto text = rpl::single(prefix + percent);
|
||||
return MakeValueWithSmallButton(table, label, std::move(text));
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakePriceValueWithTooltip(
|
||||
not_null<Ui::TableLayout*> table,
|
||||
std::shared_ptr<InfoTooltipData> data,
|
||||
TextWithEntities price,
|
||||
TextWithEntities tooltip) {
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
table,
|
||||
rpl::single(price),
|
||||
table->st().defaultValue);
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
const auto handler = [=](not_null<Ui::RpWidget*> button) {
|
||||
ShowInfoTooltip(
|
||||
data,
|
||||
button,
|
||||
rpl::single(tooltip),
|
||||
kPriceTooltipDuration);
|
||||
};
|
||||
auto text = rpl::single(u"?"_q);
|
||||
return MakeValueWithSmallButton(table, label, std::move(text), handler);
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakeMinimumPriceValue(
|
||||
not_null<Ui::TableLayout*> table,
|
||||
std::shared_ptr<InfoTooltipData> tooltip,
|
||||
const std::shared_ptr<Data::UniqueGift> &unique) {
|
||||
const auto &value = unique->value;
|
||||
const auto text = FormatValuePrice(value->minimumPrice, value->currency);
|
||||
return MakePriceValueWithTooltip(
|
||||
table,
|
||||
std::move(tooltip),
|
||||
text,
|
||||
tr::lng_gift_value_minimum_price_tooltip(
|
||||
tr::now,
|
||||
lt_amount,
|
||||
Ui::Text::Bold(text.text),
|
||||
lt_gift,
|
||||
Ui::Text::Bold(unique->title),
|
||||
Ui::Text::WithEntities));
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakeAveragePriceValue(
|
||||
not_null<Ui::TableLayout*> table,
|
||||
std::shared_ptr<InfoTooltipData> tooltip,
|
||||
const std::shared_ptr<Data::UniqueGift> &unique) {
|
||||
const auto &value = unique->value;
|
||||
const auto text = FormatValuePrice(value->averagePrice, value->currency);
|
||||
return MakePriceValueWithTooltip(
|
||||
table,
|
||||
std::move(tooltip),
|
||||
text,
|
||||
tr::lng_gift_value_average_price_tooltip(
|
||||
tr::now,
|
||||
lt_amount,
|
||||
Ui::Text::Bold(text.text),
|
||||
lt_gift,
|
||||
Ui::Text::Bold(unique->title),
|
||||
Ui::Text::WithEntities));
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakeAttributeValue(
|
||||
not_null<Ui::TableLayout*> table,
|
||||
const Data::UniqueGiftAttribute &attribute,
|
||||
Fn<void(not_null<Ui::RpWidget*>, int)> showTooltip) {
|
||||
auto result = object_ptr<Ui::RpWidget>(table);
|
||||
const auto raw = result.data();
|
||||
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
raw,
|
||||
table,
|
||||
attribute.name,
|
||||
table->st().defaultValue);
|
||||
const auto permille = attribute.rarityPermille;
|
||||
|
||||
const auto text = QString::number(permille / 10.) + '%';
|
||||
const auto rarity = Ui::CreateChild<Ui::RoundButton>(
|
||||
raw,
|
||||
rpl::single(text),
|
||||
table->st().smallButton);
|
||||
rarity->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
||||
|
||||
rpl::combine(
|
||||
raw->widthValue(),
|
||||
rarity->widthValue()
|
||||
) | rpl::start_with_next([=](int width, int convertWidth) {
|
||||
const auto convertSkip = convertWidth
|
||||
? (st::normalFont->spacew + convertWidth)
|
||||
: 0;
|
||||
label->resizeToNaturalWidth(width - convertSkip);
|
||||
label->moveToLeft(0, 0, width);
|
||||
rarity->moveToLeft(
|
||||
label->width() + st::normalFont->spacew,
|
||||
(table->st().defaultValue.style.font->ascent
|
||||
- table->st().smallButton.style.font->ascent),
|
||||
width);
|
||||
}, label->lifetime());
|
||||
|
||||
label->heightValue() | rpl::start_with_next([=](int height) {
|
||||
raw->resize(
|
||||
raw->width(),
|
||||
height + st::giveawayGiftCodeValueMargin.bottom());
|
||||
}, raw->lifetime());
|
||||
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
rarity->setClickedCallback([=] {
|
||||
showTooltip(rarity, permille);
|
||||
});
|
||||
const auto permille = attribute.rarityPermille;
|
||||
auto text = rpl::single(QString::number(permille / 10.) + '%');
|
||||
|
||||
return result;
|
||||
const auto handler = [=](not_null<Ui::RpWidget*> button) {
|
||||
showTooltip(button, permille);
|
||||
};
|
||||
return MakeValueWithSmallButton(table, label, std::move(text), handler);
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakeStarGiftStarsValue(
|
||||
@@ -409,88 +584,88 @@ void AddTableRow(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
const Data::CreditsHistoryEntry &entry,
|
||||
Fn<void()> convertToStars) {
|
||||
auto result = object_ptr<Ui::RpWidget>(table);
|
||||
const auto raw = result.data();
|
||||
|
||||
const auto star = Ui::CreateSingleStarWidget(
|
||||
raw,
|
||||
table->st().defaultValue.style.font->height);
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
raw,
|
||||
Lang::FormatCreditsAmountDecimal(entry.credits),
|
||||
auto helper = Ui::Text::CustomEmojiHelper();
|
||||
const auto price = helper.paletteDependent(Ui::Earn::IconCreditsEmoji(
|
||||
)).append(' ').append(Lang::FormatCreditsAmountDecimal(entry.credits));
|
||||
auto label = object_ptr<Ui::FlatLabel>(
|
||||
table,
|
||||
rpl::single(price),
|
||||
table->st().defaultValue,
|
||||
st::defaultPopupMenu);
|
||||
|
||||
const auto convert = convertToStars
|
||||
? Ui::CreateChild<Ui::RoundButton>(
|
||||
raw,
|
||||
tr::lng_gift_sell_small(
|
||||
lt_count_decimal,
|
||||
rpl::single(entry.starsConverted * 1.)),
|
||||
table->st().smallButton)
|
||||
: nullptr;
|
||||
if (convert) {
|
||||
using namespace Ui;
|
||||
convert->setTextTransform(RoundButton::TextTransform::NoTransform);
|
||||
convert->setClickedCallback(std::move(convertToStars));
|
||||
}
|
||||
rpl::combine(
|
||||
raw->widthValue(),
|
||||
convert ? convert->widthValue() : rpl::single(0)
|
||||
) | rpl::start_with_next([=](int width, int convertWidth) {
|
||||
const auto convertSkip = convertWidth
|
||||
? (st::normalFont->spacew + convertWidth)
|
||||
: 0;
|
||||
const auto labelLeft = rect::right(star) + st::normalFont->spacew;
|
||||
label->resizeToNaturalWidth(width - convertSkip - labelLeft);
|
||||
star->moveToLeft(0, 0, width);
|
||||
label->moveToLeft(labelLeft, 0, width);
|
||||
if (convert) {
|
||||
convert->moveToLeft(
|
||||
rect::right(label) + st::normalFont->spacew,
|
||||
(table->st().defaultValue.style.font->ascent
|
||||
- table->st().smallButton.style.font->ascent),
|
||||
width);
|
||||
}
|
||||
}, label->lifetime());
|
||||
|
||||
label->heightValue() | rpl::start_with_next([=](int height) {
|
||||
raw->resize(
|
||||
raw->width(),
|
||||
height + st::giveawayGiftCodeValueMargin.bottom());
|
||||
}, raw->lifetime());
|
||||
|
||||
st::defaultPopupMenu,
|
||||
helper.context());
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
return result;
|
||||
if (!convertToStars) {
|
||||
return label;
|
||||
}
|
||||
const auto handler = [=](not_null<Ui::RpWidget*> button) {
|
||||
convertToStars();
|
||||
};
|
||||
auto text = tr::lng_gift_sell_small(
|
||||
lt_count_decimal,
|
||||
rpl::single(entry.starsConverted * 1.));
|
||||
return MakeValueWithSmallButton(
|
||||
table,
|
||||
label.release(),
|
||||
std::move(text),
|
||||
handler);
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakeNonUniqueStatusTableValue(
|
||||
not_null<Ui::TableLayout*> table) {
|
||||
auto result = object_ptr<Ui::RpWidget>(table);
|
||||
const auto raw = result.data();
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakeUniqueGiftValueValue(
|
||||
not_null<Ui::TableLayout*> table,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
const Data::CreditsHistoryEntry &entry,
|
||||
Settings::CreditsEntryBoxStyleOverrides st) {
|
||||
const auto unique = entry.uniqueGift;
|
||||
const auto value = unique ? unique->value : nullptr;
|
||||
const auto loading = std::make_shared<bool>(false);
|
||||
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
raw,
|
||||
tr::lng_gift_unique_status_non(),
|
||||
table,
|
||||
rpl::single(
|
||||
FormatValuePrice(value->valuePrice, value->currency, true)),
|
||||
table->st().defaultValue,
|
||||
st::defaultPopupMenu);
|
||||
|
||||
raw->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
label->resizeToNaturalWidth(width);
|
||||
label->moveToLeft(0, 0, width);
|
||||
}, label->lifetime());
|
||||
|
||||
label->heightValue() | rpl::start_with_next([=](int height) {
|
||||
raw->resize(
|
||||
raw->width(),
|
||||
height + st::giveawayGiftCodeValueMargin.bottom());
|
||||
}, raw->lifetime());
|
||||
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
return result;
|
||||
const auto handler = [=](not_null<Ui::RpWidget*> button) {
|
||||
if (value->initialPriceStars) {
|
||||
show->show(Box(Settings::UniqueGiftValueBox, show, entry, st));
|
||||
} else if (*loading) {
|
||||
return;
|
||||
}
|
||||
*loading = true;
|
||||
show->session().api().request(MTPpayments_GetUniqueStarGiftValueInfo(
|
||||
MTP_string(unique->slug)
|
||||
)).done([=](const MTPpayments_UniqueStarGiftValueInfo &result) {
|
||||
*loading = false;
|
||||
|
||||
const auto &data = result.data();
|
||||
value->currency = qs(data.vcurrency());
|
||||
value->valuePrice = data.vvalue().v;
|
||||
value->initialSaleDate = data.vinitial_sale_date().v;
|
||||
value->initialPriceStars = CreditsAmount(
|
||||
data.vinitial_sale_stars().v);
|
||||
value->initialSalePrice = data.vinitial_sale_price().v;
|
||||
value->lastSaleDate = data.vlast_sale_date().value_or_empty();
|
||||
value->lastSalePrice = data.vlast_sale_price().value_or_empty();
|
||||
value->lastSaleFragment = data.is_last_sale_on_fragment();
|
||||
value->minimumPrice = data.vfloor_price().value_or_empty();
|
||||
value->averagePrice = data.vaverage_price().value_or_empty();
|
||||
value->forSaleOnTelegram = data.vlisted_count().value_or_empty();
|
||||
value->forSaleOnFragment = int(
|
||||
data.vfragment_listed_count().value_or_empty());
|
||||
value->fragmentUrl = qs(
|
||||
data.vfragment_listed_url().value_or_empty());
|
||||
|
||||
show->show(Box(Settings::UniqueGiftValueBox, show, entry, st));
|
||||
}).send();
|
||||
};
|
||||
return MakeValueWithSmallButton(
|
||||
table,
|
||||
label,
|
||||
tr::lng_gift_unique_value_learn_more(),
|
||||
handler);
|
||||
}
|
||||
|
||||
not_null<Ui::FlatLabel*> AddTableRow(
|
||||
@@ -505,11 +680,7 @@ not_null<Ui::FlatLabel*> AddTableRow(
|
||||
st::defaultPopupMenu,
|
||||
context);
|
||||
const auto result = widget.data();
|
||||
AddTableRow(
|
||||
table,
|
||||
std::move(label),
|
||||
std::move(widget),
|
||||
st::giveawayGiftCodeValueMargin);
|
||||
AddTableRow(table, std::move(label), std::move(widget));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -759,7 +930,7 @@ void GiftCodeBox(
|
||||
close->moveToRight(0, 0);
|
||||
}, box->lifetime());
|
||||
|
||||
const auto button = box->addButton(rpl::conditional(
|
||||
box->addButton(rpl::conditional(
|
||||
state->used.value(),
|
||||
tr::lng_box_ok(),
|
||||
tr::lng_gift_link_use()
|
||||
@@ -788,15 +959,6 @@ void GiftCodeBox(
|
||||
controller->session().api().premium().applyGiftCode(slug, done);
|
||||
}
|
||||
});
|
||||
const auto buttonPadding = st::giveawayGiftCodeBox.buttonPadding;
|
||||
const auto buttonWidth = st::boxWideWidth
|
||||
- buttonPadding.left()
|
||||
- buttonPadding.right();
|
||||
button->widthValue() | rpl::filter([=] {
|
||||
return (button->widthNoMargins() != buttonWidth);
|
||||
}) | rpl::start_with_next([=] {
|
||||
button->resizeToWidth(buttonWidth);
|
||||
}, button->lifetime());
|
||||
}
|
||||
|
||||
void GiftCodePendingBox(
|
||||
@@ -897,16 +1059,7 @@ void GiftCodePendingBox(
|
||||
close->moveToRight(0, 0);
|
||||
}, box->lifetime());
|
||||
|
||||
const auto button = box->addButton(tr::lng_close(), closeCallback);
|
||||
const auto buttonPadding = st::giveawayGiftCodeBox.buttonPadding;
|
||||
const auto buttonWidth = st::boxWideWidth
|
||||
- buttonPadding.left()
|
||||
- buttonPadding.right();
|
||||
button->widthValue() | rpl::filter([=] {
|
||||
return (button->widthNoMargins() != buttonWidth);
|
||||
}) | rpl::start_with_next([=] {
|
||||
button->resizeToWidth(buttonWidth);
|
||||
}, button->lifetime());
|
||||
box->addButton(tr::lng_close(), closeCallback);
|
||||
}
|
||||
|
||||
void ResolveGiftCode(
|
||||
@@ -990,12 +1143,11 @@ void GiveawayInfoBox(
|
||||
label->setTextColorOverride(st::windowActiveTextFg->c);
|
||||
}
|
||||
const auto result = box->addRow(
|
||||
object_ptr<Ui::PaddingWrap<Ui::CenterWrap<Ui::FlatLabel>>>(
|
||||
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
|
||||
box.get(),
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
box.get(),
|
||||
std::move(label)),
|
||||
QMargins(0, skip, 0, skip)));
|
||||
std::move(label),
|
||||
QMargins(0, skip, 0, skip)),
|
||||
style::al_justify);
|
||||
result->paintRequest() | rpl::start_with_next([=] {
|
||||
auto p = QPainter(result);
|
||||
p.setPen(Qt::NoPen);
|
||||
@@ -1259,64 +1411,13 @@ void AddStarGiftTable(
|
||||
const auto giftToChannel = entry.giftChannelSavedId
|
||||
&& peerIsChannel(PeerId(entry.bareEntryOwnerId));
|
||||
|
||||
const auto raw = std::make_shared<Ui::ImportantTooltip*>(nullptr);
|
||||
const auto tooltip = std::make_shared<InfoTooltipData>(InfoTooltipData{
|
||||
.parent = container,
|
||||
});
|
||||
const auto showTooltip = [=](
|
||||
not_null<Ui::RpWidget*> widget,
|
||||
rpl::producer<TextWithEntities> text) {
|
||||
if (*raw) {
|
||||
(*raw)->toggleAnimated(false);
|
||||
}
|
||||
const auto tooltip = Ui::CreateChild<Ui::ImportantTooltip>(
|
||||
container,
|
||||
Ui::MakeNiceTooltipLabel(
|
||||
container,
|
||||
std::move(text),
|
||||
st::boxWideWidth,
|
||||
st::defaultImportantTooltipLabel),
|
||||
st::defaultImportantTooltip);
|
||||
tooltip->toggleFast(false);
|
||||
|
||||
const auto update = [=] {
|
||||
const auto geometry = Ui::MapFrom(
|
||||
container,
|
||||
widget,
|
||||
widget->rect());
|
||||
const auto countPosition = [=](QSize size) {
|
||||
const auto left = geometry.x()
|
||||
+ (geometry.width() - size.width()) / 2;
|
||||
const auto right = container->width()
|
||||
- st::normalFont->spacew;
|
||||
return QPoint(
|
||||
std::max(std::min(left, right - size.width()), 0),
|
||||
geometry.y() - size.height() - st::normalFont->descent);
|
||||
};
|
||||
tooltip->pointAt(geometry, RectPart::Top, countPosition);
|
||||
};
|
||||
container->widthValue(
|
||||
) | rpl::start_with_next(update, tooltip->lifetime());
|
||||
|
||||
update();
|
||||
tooltip->toggleAnimated(true);
|
||||
|
||||
*raw = tooltip;
|
||||
tooltip->shownValue() | rpl::filter(
|
||||
!rpl::mappers::_1
|
||||
) | rpl::start_with_next([=] {
|
||||
crl::on_main(tooltip, [=] {
|
||||
if (tooltip->isHidden()) {
|
||||
if (*raw == tooltip) {
|
||||
*raw = nullptr;
|
||||
}
|
||||
delete tooltip;
|
||||
}
|
||||
});
|
||||
}, tooltip->lifetime());
|
||||
|
||||
base::timer_once(
|
||||
kRarityTooltipDuration
|
||||
) | rpl::start_with_next([=] {
|
||||
tooltip->toggleAnimated(false);
|
||||
}, tooltip->lifetime());
|
||||
ShowInfoTooltip(tooltip, widget, std::move(text), kTooltipDuration);
|
||||
};
|
||||
|
||||
if (unique && entry.bareGiftResaleRecipientId) {
|
||||
@@ -1373,8 +1474,7 @@ void AddStarGiftTable(
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_unique_owner(),
|
||||
std::move(label),
|
||||
st::giveawayGiftCodeValueMargin);
|
||||
std::move(label));
|
||||
}
|
||||
} else if (giftToChannel) {
|
||||
AddTableRow(
|
||||
@@ -1435,8 +1535,6 @@ void AddStarGiftTable(
|
||||
tr::lng_gift_link_label_date(),
|
||||
rpl::single(Ui::Text::WithEntities(langDateTime(entry.date))));
|
||||
}
|
||||
const auto marginWithButton = st::giveawayGiftCodeValueMargin
|
||||
- QMargins(0, 0, 0, st::giveawayGiftCodeValueMargin.bottom());
|
||||
if (unique) {
|
||||
const auto showRarity = [=](
|
||||
not_null<Ui::RpWidget*> widget,
|
||||
@@ -1450,18 +1548,15 @@ void AddStarGiftTable(
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_unique_model(),
|
||||
MakeAttributeValue(table, unique->model, showRarity),
|
||||
marginWithButton);
|
||||
MakeAttributeValue(table, unique->model, showRarity));
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_unique_backdrop(),
|
||||
MakeAttributeValue(table, unique->backdrop, showRarity),
|
||||
marginWithButton);
|
||||
MakeAttributeValue(table, unique->backdrop, showRarity));
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_unique_symbol(),
|
||||
MakeAttributeValue(table, unique->pattern, showRarity),
|
||||
marginWithButton);
|
||||
MakeAttributeValue(table, unique->pattern, showRarity));
|
||||
} else {
|
||||
AddTableRow(
|
||||
table,
|
||||
@@ -1470,8 +1565,7 @@ void AddStarGiftTable(
|
||||
table,
|
||||
show,
|
||||
entry,
|
||||
std::move(convertToStars)),
|
||||
marginWithButton);
|
||||
std::move(convertToStars)));
|
||||
}
|
||||
if (entry.limitedCount > 0 && !entry.giftRefunded) {
|
||||
auto amount = rpl::single(TextWithEntities{
|
||||
@@ -1503,10 +1597,15 @@ void AddStarGiftTable(
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_unique_status(),
|
||||
MakeNonUniqueStatusTableValue(table),
|
||||
marginWithButton);
|
||||
tr::lng_gift_unique_status_non(Ui::Text::WithEntities));
|
||||
}
|
||||
if (unique) {
|
||||
if (unique->value) {
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_unique_value(),
|
||||
MakeUniqueGiftValueValue(table, show, entry, st));
|
||||
}
|
||||
const auto &original = unique->originalDetails;
|
||||
if (original.recipientId) {
|
||||
const auto owner = &show->session().data();
|
||||
@@ -1691,8 +1790,7 @@ void AddCreditsHistoryEntryTable(
|
||||
(entry.reaction
|
||||
? tr::lng_credits_box_history_entry_message
|
||||
: tr::lng_credits_box_history_entry_media)(),
|
||||
std::move(label),
|
||||
st::giveawayGiftCodeValueMargin);
|
||||
std::move(label));
|
||||
}
|
||||
}
|
||||
using Type = Data::CreditsHistoryEntry::PeerType;
|
||||
@@ -1799,8 +1897,7 @@ void AddCreditsHistoryEntryTable(
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_credits_box_history_entry_id(),
|
||||
std::move(label),
|
||||
st::giveawayGiftCodeValueMargin);
|
||||
std::move(label));
|
||||
}
|
||||
if (entry.floodSkip) {
|
||||
AddTableRow(
|
||||
@@ -1988,7 +2085,71 @@ void AddChannelEarnTable(
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_credits_box_history_entry_id(),
|
||||
std::move(label),
|
||||
st::giveawayGiftCodeValueMargin);
|
||||
std::move(label));
|
||||
}
|
||||
}
|
||||
|
||||
void AddUniqueGiftValueTable(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
Settings::CreditsEntryBoxStyleOverrides st,
|
||||
const Data::CreditsHistoryEntry &entry) {
|
||||
const auto value = entry.uniqueGift ? entry.uniqueGift->value : nullptr;
|
||||
auto table = container->add(
|
||||
object_ptr<Ui::TableLayout>(
|
||||
container,
|
||||
st.table ? *st.table : st::giveawayGiftCodeTable),
|
||||
st::giveawayGiftCodeTableMargin);
|
||||
if (value->initialSaleDate) {
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_value_initial_sale(),
|
||||
rpl::single(FormatValueDate(value->initialSaleDate)));
|
||||
}
|
||||
auto helper = Ui::Text::CustomEmojiHelper();
|
||||
auto starIcon = helper.paletteDependent(
|
||||
Ui::Earn::IconCreditsEmoji());
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_value_initial_price(),
|
||||
tr::lng_gift_value_initial_price_value(
|
||||
lt_stars,
|
||||
rpl::single(starIcon.append(' ').append(
|
||||
Lang::FormatCreditsAmountDecimal(value->initialPriceStars)
|
||||
)),
|
||||
lt_amount,
|
||||
rpl::single(FormatValuePrice(
|
||||
value->initialSalePrice,
|
||||
value->currency,
|
||||
true)),
|
||||
Ui::Text::WithEntities),
|
||||
helper.context());
|
||||
if (value->lastSaleDate) {
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_value_last_sale(),
|
||||
rpl::single(FormatValueDate(value->lastSaleDate)));
|
||||
}
|
||||
if (value->lastSalePrice) {
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_value_last_price(),
|
||||
MakePriceWithChangePercentValue(table, value));
|
||||
}
|
||||
|
||||
const auto tooltip = std::make_shared<InfoTooltipData>(InfoTooltipData{
|
||||
.parent = container,
|
||||
});
|
||||
if (value->minimumPrice) {
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_value_minimum_price(),
|
||||
MakeMinimumPriceValue(table, tooltip, entry.uniqueGift));
|
||||
}
|
||||
if (value->averagePrice) {
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_vlaue_average_price(),
|
||||
MakeAveragePriceValue(table, tooltip, entry.uniqueGift));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,3 +106,9 @@ void AddChannelEarnTable(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
const Data::CreditsHistoryEntry &entry);
|
||||
|
||||
void AddUniqueGiftValueTable(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
Settings::CreditsEntryBoxStyleOverrides st,
|
||||
const Data::CreditsHistoryEntry &entry);
|
||||
|
||||
@@ -1460,7 +1460,8 @@ void PeerListContent::setSearchMode(PeerListSearchMode mode) {
|
||||
_loadingAnimation = Ui::CreateLoadingPeerListItemWidget(
|
||||
this,
|
||||
_st.item,
|
||||
2);
|
||||
2,
|
||||
_controller->computeListSt().bg->c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -513,7 +513,7 @@ void InviteForbiddenController::setComplexCover() {
|
||||
container->add(
|
||||
MakeShowOrLabel(container, tr::lng_invite_upgrade_or()),
|
||||
st::inviteForbiddenOrLabelPadding,
|
||||
style::al_top);
|
||||
style::al_justify);
|
||||
}
|
||||
container->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
|
||||
@@ -77,7 +77,9 @@ DefaultIconEmoji::DefaultIconEmoji(
|
||||
std::move(value) | rpl::start_with_next([=](DefaultIcon value) {
|
||||
_icon = value;
|
||||
_image = QImage();
|
||||
repaint();
|
||||
if (repaint) {
|
||||
repaint();
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
|
||||
@@ -1200,7 +1200,7 @@ void EditPeerColorBox(
|
||||
peer,
|
||||
state->index.value(),
|
||||
state->emojiId.value()
|
||||
), {});
|
||||
), style::margins());
|
||||
|
||||
auto indices = peer->session().api().peerColors().suggestedValue();
|
||||
const auto margin = st::settingsColorRadioMargin;
|
||||
|
||||
@@ -969,9 +969,9 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
|
||||
const auto photoSize = st::boostReplaceUserpic.photoSize;
|
||||
const auto session = &row->peer()->session();
|
||||
content->add(object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
Settings::SubscriptionUserpic(content, channel, photoSize)));
|
||||
content->add(
|
||||
Settings::SubscriptionUserpic(content, channel, photoSize),
|
||||
style::al_top);
|
||||
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
@@ -981,7 +981,6 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
box,
|
||||
tr::lng_credits_box_subscription_title(),
|
||||
st::creditsBoxAboutTitle),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
Ui::AddSkip(content);
|
||||
@@ -990,7 +989,6 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
st::creditsTopupPrice),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
subtitle1->setMarkedText(
|
||||
tr::lng_credits_subscription_subtitle(
|
||||
@@ -1002,11 +1000,10 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
Ui::Text::WithEntities),
|
||||
_emojiHelper.context());
|
||||
const auto subtitle2 = box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
st::creditsTopupPrice)))->entity();
|
||||
st::creditsTopupPrice),
|
||||
style::al_top);
|
||||
session->credits().rateValue(
|
||||
channel
|
||||
) | rpl::start_with_next([=, currency = u"USD"_q](float64 rate) {
|
||||
@@ -1038,19 +1035,11 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
tr::lng_credits_box_out_about_link(tr::now)),
|
||||
Ui::Text::WithEntities),
|
||||
st::creditsBoxAboutDivider),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
const auto button = box->addButton(tr::lng_box_ok(), [=] {
|
||||
box->addButton(tr::lng_box_ok(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
const auto buttonWidth = st::boxWidth
|
||||
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
|
||||
button->widthValue() | rpl::filter([=] {
|
||||
return (button->widthNoMargins() != buttonWidth);
|
||||
}) | rpl::start_with_next([=] {
|
||||
button->resizeToWidth(buttonWidth);
|
||||
}, button->lifetime());
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -585,14 +585,14 @@ void ChannelsLimitBox(
|
||||
|
||||
const auto content = box->addRow(
|
||||
object_ptr<PeerListContent>(box, controller),
|
||||
{});
|
||||
style::margins());
|
||||
delegate->setContent(content);
|
||||
controller->setDelegate(delegate);
|
||||
|
||||
const auto count = 100;
|
||||
const auto placeholder = box->addRow(
|
||||
object_ptr<PeerListDummy>(box, count, st::defaultPeerList),
|
||||
{});
|
||||
style::margins());
|
||||
|
||||
using namespace rpl::mappers;
|
||||
controller->countValue(
|
||||
@@ -676,14 +676,14 @@ void PublicLinksLimitBox(
|
||||
|
||||
const auto content = box->addRow(
|
||||
object_ptr<PeerListContent>(box, controller),
|
||||
{});
|
||||
style::margins());
|
||||
delegate->setContent(content);
|
||||
controller->setDelegate(delegate);
|
||||
|
||||
const auto count = defaultLimit;
|
||||
const auto placeholder = box->addRow(
|
||||
object_ptr<PeerListDummy>(box, count, st::defaultPeerList),
|
||||
{});
|
||||
style::margins());
|
||||
|
||||
using namespace rpl::mappers;
|
||||
controller->countValue(
|
||||
|
||||
@@ -902,7 +902,7 @@ void PreviewBox(
|
||||
|
||||
const auto outer = box->addRow(
|
||||
ChatBackPreview(box, size.height(), back),
|
||||
{});
|
||||
style::margins());
|
||||
|
||||
struct Hiding {
|
||||
not_null<Ui::RpWidget*> widget;
|
||||
|
||||
@@ -7,8 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/ringtones_box.h"
|
||||
|
||||
#include "data/notify/data_peer_notify_volume.h"
|
||||
#include "data/notify/data_peer_notify_settings.h"
|
||||
#include "api/api_ringtones.h"
|
||||
#include "apiwrap.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "base/timer_rpl.h"
|
||||
@@ -21,11 +24,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_document_resolver.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "media/audio/media_audio.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
@@ -111,7 +117,8 @@ void RingtonesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session,
|
||||
Data::NotifySound selected,
|
||||
Fn<void(Data::NotifySound)> save) {
|
||||
Fn<void(Data::NotifySound)> save,
|
||||
Data::VolumeController volumeController) {
|
||||
box->setTitle(tr::lng_ringtones_box_title());
|
||||
|
||||
const auto container = box->verticalLayout();
|
||||
@@ -128,12 +135,17 @@ void RingtonesBox(
|
||||
QPointer<Ui::Radiobutton> defaultButton;
|
||||
QPointer<Ui::Radiobutton> chosenButton;
|
||||
std::vector<QPointer<Ui::Radiobutton>> buttons;
|
||||
ushort presavedVolume = 0;
|
||||
};
|
||||
const auto state = container->lifetime().make_state<State>(State{
|
||||
.group = std::make_shared<Ui::RadiobuttonGroup>(),
|
||||
.chosen = selected,
|
||||
});
|
||||
|
||||
const auto volumeOverride = [volume = volumeController.volume] {
|
||||
return volume ? (0.01 * volume()) : -1;
|
||||
};
|
||||
|
||||
const auto addToGroup = [=](
|
||||
not_null<Ui::VerticalLayout*> verticalLayout,
|
||||
int value,
|
||||
@@ -156,7 +168,10 @@ void RingtonesBox(
|
||||
if (value == kDefaultValue) {
|
||||
state->defaultButton = button;
|
||||
button->setClickedCallback([=] {
|
||||
Core::App().notifications().playSound(session, 0);
|
||||
Core::App().notifications().playSound(
|
||||
session,
|
||||
0,
|
||||
volumeOverride());
|
||||
});
|
||||
}
|
||||
if (value < 0) {
|
||||
@@ -170,7 +185,8 @@ void RingtonesBox(
|
||||
if (media->loaded()) {
|
||||
Core::App().notifications().playSound(
|
||||
session,
|
||||
media->owner()->id);
|
||||
media->owner()->id,
|
||||
volumeOverride());
|
||||
}
|
||||
});
|
||||
base::install_event_filter(button, [=](not_null<QEvent*> e) {
|
||||
@@ -320,6 +336,31 @@ void RingtonesBox(
|
||||
}));
|
||||
});
|
||||
|
||||
if (volumeController.volume && volumeController.saveVolume) {
|
||||
auto saveAndTestVolume = [=](ushort currentVolume) {
|
||||
state->presavedVolume = currentVolume;
|
||||
const auto value = state->group->current();
|
||||
if (value != kNoSoundValue) {
|
||||
Core::App().notifications().playSound(
|
||||
session,
|
||||
(value == kDefaultValue)
|
||||
? 0
|
||||
: state->medias[value]->owner()->id,
|
||||
0.01 * currentVolume);
|
||||
}
|
||||
};
|
||||
Ui::AddRingtonesVolumeSlider(
|
||||
container,
|
||||
state->group->value() | rpl::map([=](int value) {
|
||||
return value != kNoSoundValue;
|
||||
}),
|
||||
tr::lng_ringtones_box_volume(),
|
||||
Data::VolumeController{
|
||||
base::take(volumeController.volume),
|
||||
std::move(saveAndTestVolume),
|
||||
});
|
||||
}
|
||||
|
||||
box->addSkip(st::ringtonesBoxSkip);
|
||||
Ui::AddDividerText(container, tr::lng_ringtones_box_about());
|
||||
|
||||
@@ -333,6 +374,9 @@ void RingtonesBox(
|
||||
: (value == kNoSoundValue)
|
||||
? Data::NotifySound{ .none = true }
|
||||
: Data::NotifySound{ .id = state->medias[value]->owner()->id };
|
||||
if (state->presavedVolume) {
|
||||
volumeController.saveVolume(state->presavedVolume);
|
||||
}
|
||||
save(sound);
|
||||
box->closeBox();
|
||||
});
|
||||
@@ -345,5 +389,5 @@ void ThreadRingtonesBox(
|
||||
const auto now = thread->owner().notifySettings().sound(thread);
|
||||
RingtonesBox(box, &thread->session(), now, [=](Data::NotifySound sound) {
|
||||
thread->owner().notifySettings().update(thread, {}, {}, sound);
|
||||
});
|
||||
}, Data::ThreadRingtonesVolumeController(thread));
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Data {
|
||||
struct NotifySound;
|
||||
class Thread;
|
||||
enum class DefaultNotify : uint8_t;
|
||||
struct VolumeController;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
@@ -28,7 +30,8 @@ void RingtonesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session,
|
||||
Data::NotifySound selected,
|
||||
Fn<void(Data::NotifySound)> save);
|
||||
Fn<void(Data::NotifySound)> save,
|
||||
Data::VolumeController volumeController);
|
||||
|
||||
void ThreadRingtonesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
|
||||
@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/passcode_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
@@ -148,6 +149,8 @@ void SelfDestructionBox::showContent() {
|
||||
: tr::lng_self_destruct_sessions_description(tr::now)),
|
||||
st::boxLabel);
|
||||
_description->moveToLeft(st::boxPadding.left(), y);
|
||||
_description->resizeToWidth(st::boxWidth
|
||||
- rect::m::sum::h(st::boxPadding));
|
||||
y += _description->height() + st::boxMediumSkip;
|
||||
|
||||
for (const auto value : _ttlValues) {
|
||||
@@ -200,6 +203,8 @@ void SelfDestructionBox::prepare() {
|
||||
? tr::lng_self_destruct_description(tr::now)
|
||||
: tr::lng_self_destruct_sessions_description(tr::now)),
|
||||
st::boxLabel);
|
||||
fake->resizeToWidth(st::boxWidth
|
||||
- rect::m::sum::h(st::boxPadding));
|
||||
const auto boxHeight = st::boxOptionListPadding.top()
|
||||
+ fake->height() + st::boxMediumSkip
|
||||
+ (_ttlValues.size()
|
||||
|
||||
@@ -348,9 +348,9 @@ void SendCreditsBox(
|
||||
}, ministarsContainer->lifetime());
|
||||
}
|
||||
|
||||
const auto thumb = box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
SendCreditsThumbnail(content, session, form.get(), photoSize)));
|
||||
const auto thumb = box->addRow(
|
||||
SendCreditsThumbnail(content, session, form.get(), photoSize),
|
||||
style::al_top);
|
||||
thumb->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
if (form->invoice.subscriptionPeriod) {
|
||||
const auto badge = SendCreditsBadge(content, form->invoice.amount);
|
||||
@@ -371,16 +371,14 @@ void SendCreditsBox(
|
||||
? rpl::single(form->title)
|
||||
: tr::lng_credits_box_out_title(),
|
||||
st::settingsPremiumUserTitle),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
if (form->invoice.subscriptionPeriod && form->botId && form->photo) {
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
const auto bot = session->data().user(form->botId);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
Ui::CreatePeerBubble(box, bot)));
|
||||
Ui::CreatePeerBubble(box, bot),
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
@@ -389,7 +387,6 @@ void SendCreditsBox(
|
||||
box,
|
||||
SendCreditsConfirmText(session, form.get()),
|
||||
st::creditsBoxAbout),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
@@ -467,14 +464,6 @@ void SendCreditsBox(
|
||||
st::creditsBoxButtonLabel,
|
||||
&box->getDelegate()->style().button.textFg);
|
||||
|
||||
const auto buttonWidth = st::boxWidth
|
||||
- rect::m::sum::h(stBox.buttonPadding);
|
||||
button->widthValue() | rpl::filter([=] {
|
||||
return (button->widthNoMargins() != buttonWidth);
|
||||
}) | rpl::start_with_next([=] {
|
||||
button->resizeToWidth(buttonWidth);
|
||||
}, button->lifetime());
|
||||
|
||||
{
|
||||
const auto close = Ui::CreateChild<Ui::IconButton>(
|
||||
content,
|
||||
|
||||
@@ -1935,6 +1935,29 @@ void FastShareMessage(
|
||||
}), Ui::LayerOption::CloseOther);
|
||||
}
|
||||
|
||||
void FastShareMessageToSelf(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<HistoryItem*> item) {
|
||||
const auto self = show->session().user();
|
||||
const auto donePhraseArgs = ChatHelpers::ForwardedMessagePhraseArgs{
|
||||
.toCount = 1,
|
||||
.singleMessage = true,
|
||||
.to1 = self,
|
||||
.to2 = nullptr,
|
||||
};
|
||||
auto sendAction = Api::SendAction(self->owner().history(self));
|
||||
sendAction.clearDraft = false;
|
||||
show->session().api().forwardMessages(
|
||||
Data::ResolvedForwardDraft{ .items = {item} },
|
||||
std::move(sendAction),
|
||||
[=] {
|
||||
auto phrase = rpl::variable<TextWithEntities>(
|
||||
ChatHelpers::ForwardedMessagePhrase(
|
||||
donePhraseArgs)).current();
|
||||
show->showToast(std::move(phrase));
|
||||
});
|
||||
}
|
||||
|
||||
void FastShareMessage(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<HistoryItem*> item,
|
||||
|
||||
@@ -71,6 +71,9 @@ struct ShareBoxStyleOverrides {
|
||||
};
|
||||
[[nodiscard]] ShareBoxStyleOverrides DarkShareBoxStyle();
|
||||
|
||||
void FastShareMessageToSelf(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<HistoryItem*> item);
|
||||
void FastShareMessage(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<HistoryItem*> item,
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_credits.h"
|
||||
#include "api/api_global_privacy.h"
|
||||
#include "api/api_premium.h"
|
||||
#include "api/api_text_entities.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "base/qt_signal_producer.h"
|
||||
#include "base/random.h"
|
||||
@@ -70,6 +71,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "settings/settings_credits_graphics.h"
|
||||
#include "settings/settings_premium.h"
|
||||
#include "ui/boxes/boost_box.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/controls/emoji_button.h"
|
||||
@@ -1160,7 +1162,9 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||
const auto user = session->user();
|
||||
const auto requestId = session->api().request(
|
||||
MTPpayments_GetSavedStarGifts(
|
||||
MTP_flags(Flag::f_exclude_limited | Flag::f_exclude_unlimited),
|
||||
MTP_flags(Flag::f_exclude_upgradable
|
||||
| Flag::f_exclude_unupgradable
|
||||
| Flag::f_exclude_unlimited),
|
||||
user->input,
|
||||
MTP_int(0), // collection_id
|
||||
MTP_string(offset),
|
||||
@@ -1256,7 +1260,7 @@ struct ResaleTabs {
|
||||
object_ptr<RpWidget> widget;
|
||||
};
|
||||
[[nodiscard]] ResaleTabs MakeResaleTabs(
|
||||
not_null<Window::SessionController*> window,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
const ResaleGiftsDescriptor &info,
|
||||
rpl::producer<ResaleFilter> filter) {
|
||||
@@ -1338,7 +1342,7 @@ struct ResaleTabs {
|
||||
action->setClickedCallback(std::move(callback));
|
||||
menu->addAction(std::move(action));
|
||||
};
|
||||
auto context = Core::TextContext({ .session = &window->session() });
|
||||
auto context = Core::TextContext({ .session = &show->session() });
|
||||
context.customEmojiFactory = [original = context.customEmojiFactory](
|
||||
QStringView data,
|
||||
const Ui::Text::MarkedContext &context) {
|
||||
@@ -2084,6 +2088,23 @@ void ShowGiftUpgradedToast(
|
||||
}
|
||||
}
|
||||
|
||||
void ShowUpgradeGiftedToast(
|
||||
base::weak_ptr<Window::SessionController> weak,
|
||||
not_null<PeerData*> peer) {
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->showToast({
|
||||
.title = tr::lng_gift_upgrade_gifted_title(tr::now),
|
||||
.text = { (peer->isBroadcast()
|
||||
? tr::lng_gift_upgrade_gifted_about_channel
|
||||
: tr::lng_gift_upgrade_gifted_about)(
|
||||
tr::now,
|
||||
lt_name,
|
||||
peer->shortName()) },
|
||||
.duration = kUpgradeDoneToastDuration,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void SendStarsFormRequest(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
Settings::SmallBalanceResult result,
|
||||
@@ -2128,9 +2149,17 @@ void UpgradeGift(
|
||||
auto formDone = [=](
|
||||
Payments::CheckoutResult result,
|
||||
const MTPUpdates *updates) {
|
||||
if (result == Payments::CheckoutResult::Paid && updates) {
|
||||
if (result == Payments::CheckoutResult::Paid) {
|
||||
if (const auto strong = weak.get()) {
|
||||
ShowGiftUpgradedToast(strong, session, *updates);
|
||||
const auto owner = savedId.isUser()
|
||||
? strong->session().user().get()
|
||||
: savedId.chat();
|
||||
if (owner) {
|
||||
owner->owner().nextForUpgradeGiftInvalidate(owner);
|
||||
}
|
||||
}
|
||||
if (updates) {
|
||||
ShowGiftUpgradedToast(weak, session, *updates);
|
||||
}
|
||||
}
|
||||
done(result);
|
||||
@@ -2160,6 +2189,29 @@ void UpgradeGift(
|
||||
std::move(formDone));
|
||||
}
|
||||
|
||||
void GiftUpgrade(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer,
|
||||
QString giftPrepayUpgradeHash,
|
||||
int stars,
|
||||
Fn<void(Payments::CheckoutResult)> done) {
|
||||
const auto weak = base::make_weak(window);
|
||||
auto formDone = [=](
|
||||
Payments::CheckoutResult result,
|
||||
const MTPUpdates *updates) {
|
||||
if (result == Payments::CheckoutResult::Paid) {
|
||||
ShowUpgradeGiftedToast(weak, peer);
|
||||
}
|
||||
done(result);
|
||||
};
|
||||
RequestStarsFormAndSubmit(
|
||||
window->uiShow(),
|
||||
MTP_inputInvoiceStarGiftPrepaidUpgrade(
|
||||
peer->input,
|
||||
MTP_string(giftPrepayUpgradeHash)),
|
||||
std::move(formDone));
|
||||
}
|
||||
|
||||
void SoldOutBox(
|
||||
not_null<GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
@@ -2314,6 +2366,27 @@ void AddSoldLeftSlider(
|
||||
}, slider->lifetime());
|
||||
}
|
||||
|
||||
void CheckMaybeGiftLocked(
|
||||
not_null<Window::SessionController*> window,
|
||||
uint64 giftId,
|
||||
Fn<void()> send) {
|
||||
const auto session = &window->session();
|
||||
session->api().request(MTPpayments_CheckCanSendGift(
|
||||
MTP_long(giftId)
|
||||
)).done(crl::guard(window, [=](
|
||||
const MTPpayments_CheckCanSendGiftResult &result) {
|
||||
result.match([&](const MTPDpayments_checkCanSendGiftResultOk &) {
|
||||
send();
|
||||
}, [&](const MTPDpayments_checkCanSendGiftResultFail &data) {
|
||||
window->show(Ui::MakeInformBox({
|
||||
.text = Api::ParseTextWithEntities(session, data.vreason()),
|
||||
.title = tr::lng_gift_locked_title(),
|
||||
}));
|
||||
});
|
||||
})).fail(crl::guard(window, [=] {
|
||||
})).send();
|
||||
}
|
||||
|
||||
void SendGiftBox(
|
||||
not_null<GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
@@ -2687,13 +2760,6 @@ void SendGiftBox(
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::lifetime ShowStarGiftResale(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
uint64 giftId,
|
||||
QString title,
|
||||
Fn<void()> finishRequesting);
|
||||
|
||||
[[nodiscard]] object_ptr<RpWidget> MakeGiftsList(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer,
|
||||
@@ -2784,21 +2850,41 @@ void SendGiftBox(
|
||||
raw,
|
||||
&state->delegate);
|
||||
}
|
||||
const auto raw = button.get();
|
||||
if (validated[index]) {
|
||||
return;
|
||||
}
|
||||
button->show();
|
||||
raw->show();
|
||||
validated[index] = true;
|
||||
const auto &descriptor = state->list[state->order[index]];
|
||||
button->setDescriptor(descriptor, GiftButton::Mode::Full);
|
||||
button->setClickedCallback([=] {
|
||||
raw->setDescriptor(descriptor, GiftButton::Mode::Full);
|
||||
raw->setClickedCallback([=] {
|
||||
const auto star = std::get_if<GiftTypeStars>(&descriptor);
|
||||
const auto send = crl::guard(raw, [=] {
|
||||
window->show(Box(
|
||||
SendGiftBox,
|
||||
window,
|
||||
peer,
|
||||
state->api,
|
||||
descriptor));
|
||||
});
|
||||
const auto unique = star ? star->info.unique : nullptr;
|
||||
if (star
|
||||
&& star->info.requirePremium
|
||||
&& !peer->session().premium()) {
|
||||
const auto premiumNeeded = star && star->info.requirePremium;
|
||||
if (premiumNeeded && !peer->session().premium()) {
|
||||
Settings::ShowPremiumGiftPremium(window, star->info);
|
||||
return;
|
||||
} else if (star
|
||||
&& star->info.lockedUntilDate
|
||||
&& star->info.lockedUntilDate > base::unixtime::now()) {
|
||||
const auto ready = crl::guard(raw, [=] {
|
||||
if (premiumNeeded && !peer->session().premium()) {
|
||||
Settings::ShowPremiumGiftPremium(
|
||||
window,
|
||||
v::get<GiftTypeStars>(descriptor).info);
|
||||
} else {
|
||||
send();
|
||||
}
|
||||
});
|
||||
CheckMaybeGiftLocked(window, star->info.id, ready);
|
||||
} else if (unique && star->mine && !peer->isSelf()) {
|
||||
if (ShowTransferGiftLater(window->uiShow(), unique)) {
|
||||
return;
|
||||
@@ -2842,7 +2928,7 @@ void SendGiftBox(
|
||||
Api::InputSavedStarGiftId(savedId, unique),
|
||||
peer->input),
|
||||
formReady);
|
||||
} else if (star && star->info.unique && star->resale) {
|
||||
} else if (unique && star->resale) {
|
||||
window->show(Box(
|
||||
Settings::GlobalStarGiftBox,
|
||||
window->uiShow(),
|
||||
@@ -2867,15 +2953,10 @@ void SendGiftBox(
|
||||
} else if (star && IsSoldOut(star->info)) {
|
||||
window->show(Box(SoldOutBox, window, *star));
|
||||
} else {
|
||||
window->show(Box(
|
||||
SendGiftBox,
|
||||
window,
|
||||
peer,
|
||||
state->api,
|
||||
descriptor));
|
||||
send();
|
||||
}
|
||||
});
|
||||
button->setGeometry(QRect(QPoint(x, y), single), extend);
|
||||
raw->setGeometry(QRect(QPoint(x, y), single), extend);
|
||||
};
|
||||
y += rowFrom * singleh;
|
||||
for (auto row = rowFrom; row != rowTill; ++row) {
|
||||
@@ -3151,10 +3232,9 @@ void GiftBox(
|
||||
&& uniqueDisallowed;
|
||||
|
||||
content->add(
|
||||
object_ptr<CenterWrap<UserpicButton>>(
|
||||
content,
|
||||
object_ptr<UserpicButton>(content, peer, stUser))
|
||||
)->entity()->setClickedCallback([=] { window->showPeerInfo(peer); });
|
||||
object_ptr<UserpicButton>(content, peer, stUser),
|
||||
style::al_top
|
||||
)->setClickedCallback([=] { window->showPeerInfo(peer); });
|
||||
AddSkip(content);
|
||||
AddSkip(content);
|
||||
|
||||
@@ -3312,7 +3392,7 @@ void GiftResaleBox(
|
||||
}, content->lifetime());
|
||||
|
||||
auto tabs = MakeResaleTabs(
|
||||
window,
|
||||
window->uiShow(),
|
||||
peer,
|
||||
state->data,
|
||||
state->filter.value());
|
||||
@@ -3398,30 +3478,6 @@ void GiftResaleBox(
|
||||
}));
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::lifetime ShowStarGiftResale(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
uint64 giftId,
|
||||
QString title,
|
||||
Fn<void()> finishRequesting) {
|
||||
const auto weak = base::make_weak(controller);
|
||||
const auto session = &controller->session();
|
||||
return ResaleGiftsSlice(
|
||||
session,
|
||||
giftId
|
||||
) | rpl::start_with_next([=](ResaleGiftsDescriptor &&info) {
|
||||
if (const auto onstack = finishRequesting) {
|
||||
onstack();
|
||||
}
|
||||
if (!info.giftId || !info.count) {
|
||||
return;
|
||||
}
|
||||
info.title = title;
|
||||
controller->show(
|
||||
Box(GiftResaleBox, controller, peer, std::move(info)));
|
||||
});
|
||||
}
|
||||
|
||||
struct CustomList {
|
||||
object_ptr<Ui::RpWidget> content = { nullptr };
|
||||
Fn<bool(int, int, int)> overrideKey;
|
||||
@@ -4895,15 +4951,33 @@ void UpgradeBox(
|
||||
|
||||
infoRow(
|
||||
tr::lng_gift_upgrade_unique_title(),
|
||||
tr::lng_gift_upgrade_unique_about(),
|
||||
(args.savedId
|
||||
? tr::lng_gift_upgrade_unique_about()
|
||||
: (args.peer->isBroadcast()
|
||||
? tr::lng_gift_upgrade_unique_about_channel
|
||||
: tr::lng_gift_upgrade_unique_about_user)(
|
||||
lt_name,
|
||||
rpl::single(args.peer->shortName()))),
|
||||
&st::menuIconUnique);
|
||||
infoRow(
|
||||
tr::lng_gift_upgrade_transferable_title(),
|
||||
tr::lng_gift_upgrade_transferable_about(),
|
||||
(args.savedId
|
||||
? tr::lng_gift_upgrade_transferable_about()
|
||||
: (args.peer->isBroadcast()
|
||||
? tr::lng_gift_upgrade_transferable_about_channel
|
||||
: tr::lng_gift_upgrade_transferable_about_user)(
|
||||
lt_name,
|
||||
rpl::single(args.peer->shortName()))),
|
||||
&st::menuIconReplace);
|
||||
infoRow(
|
||||
tr::lng_gift_upgrade_tradable_title(),
|
||||
tr::lng_gift_upgrade_tradable_about(),
|
||||
(args.savedId
|
||||
? tr::lng_gift_upgrade_tradable_about()
|
||||
: (args.peer->isBroadcast()
|
||||
? tr::lng_gift_upgrade_tradable_about_channel
|
||||
: tr::lng_gift_upgrade_tradable_about_user)(
|
||||
lt_name,
|
||||
rpl::single(args.peer->shortName()))),
|
||||
&st::menuIconTradable);
|
||||
|
||||
struct State {
|
||||
@@ -4911,33 +4985,37 @@ void UpgradeBox(
|
||||
bool preserveDetails = false;
|
||||
};
|
||||
const auto state = std::make_shared<State>();
|
||||
const auto preview = !args.savedId;
|
||||
const auto gifting = !args.savedId
|
||||
&& !args.giftPrepayUpgradeHash.isEmpty();
|
||||
const auto preview = !args.savedId && !gifting;
|
||||
|
||||
if (!preview) {
|
||||
if (!preview && !gifting) {
|
||||
const auto skip = st::defaultVerticalListSkip;
|
||||
container->add(
|
||||
object_ptr<PlainShadow>(container),
|
||||
st::boxRowPadding + QMargins(0, skip, 0, skip));
|
||||
const auto checkbox = container->add(
|
||||
object_ptr<CenterWrap<Checkbox>>(
|
||||
object_ptr<Checkbox>(
|
||||
container,
|
||||
object_ptr<Checkbox>(
|
||||
container,
|
||||
(args.canAddComment
|
||||
? tr::lng_gift_upgrade_add_comment(tr::now)
|
||||
: args.canAddSender
|
||||
? tr::lng_gift_upgrade_add_sender(tr::now)
|
||||
: args.canAddMyComment
|
||||
? tr::lng_gift_upgrade_add_my_comment(tr::now)
|
||||
: tr::lng_gift_upgrade_add_my(tr::now)),
|
||||
args.addDetailsDefault)),
|
||||
st::defaultCheckbox.margin)->entity();
|
||||
(args.canAddComment
|
||||
? tr::lng_gift_upgrade_add_comment(tr::now)
|
||||
: args.canAddSender
|
||||
? tr::lng_gift_upgrade_add_sender(tr::now)
|
||||
: args.canAddMyComment
|
||||
? tr::lng_gift_upgrade_add_my_comment(tr::now)
|
||||
: tr::lng_gift_upgrade_add_my(tr::now)),
|
||||
args.addDetailsDefault),
|
||||
st::defaultCheckbox.margin,
|
||||
style::al_top);
|
||||
checkbox->checkedChanges() | rpl::start_with_next([=](bool checked) {
|
||||
state->preserveDetails = checked;
|
||||
}, checkbox->lifetime());
|
||||
}
|
||||
|
||||
box->setStyle(preview ? st::giftBox : st::upgradeGiftBox);
|
||||
if (gifting) {
|
||||
box->setWidth(st::boxWideWidth);
|
||||
}
|
||||
|
||||
const auto cost = args.cost;
|
||||
auto buttonText = preview ? tr::lng_box_ok() : rpl::single(QString());
|
||||
@@ -4961,11 +5039,18 @@ void UpgradeBox(
|
||||
}
|
||||
}
|
||||
};
|
||||
UpgradeGift(controller, args.savedId, keepDetails, cost, done);
|
||||
if (gifting) {
|
||||
GiftUpgrade(
|
||||
controller,
|
||||
args.peer,
|
||||
args.giftPrepayUpgradeHash,
|
||||
cost,
|
||||
done);
|
||||
} else {
|
||||
UpgradeGift(controller, args.savedId, keepDetails, cost, done);
|
||||
}
|
||||
});
|
||||
if (!preview) {
|
||||
auto helper = Ui::Text::CustomEmojiHelper();
|
||||
auto star = helper.paletteDependent(Ui::Earn::IconCreditsEmoji());
|
||||
SetButtonMarkedLabel(
|
||||
button,
|
||||
(cost
|
||||
@@ -4977,7 +5062,7 @@ void UpgradeBox(
|
||||
CreditsAmount{ cost }))),
|
||||
Ui::Text::WithEntities)
|
||||
: tr::lng_gift_upgrade_confirm(Ui::Text::WithEntities)),
|
||||
helper.context(),
|
||||
{},
|
||||
st::creditsBoxButtonLabel,
|
||||
&st::giftBox.button.textFg);
|
||||
}
|
||||
@@ -5341,4 +5426,30 @@ void ShowResaleGiftBoughtToast(
|
||||
.duration = kUpgradeDoneToastDuration,
|
||||
});
|
||||
}
|
||||
|
||||
rpl::lifetime ShowStarGiftResale(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
uint64 giftId,
|
||||
QString title,
|
||||
Fn<void()> finishRequesting) {
|
||||
const auto weak = base::make_weak(controller);
|
||||
const auto session = &controller->session();
|
||||
return ResaleGiftsSlice(
|
||||
session,
|
||||
giftId
|
||||
) | rpl::start_with_next([=](ResaleGiftsDescriptor &&info) {
|
||||
if (const auto onstack = finishRequesting) {
|
||||
onstack();
|
||||
}
|
||||
if (!info.giftId || !info.count) {
|
||||
return;
|
||||
}
|
||||
info.title = title;
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->show(Box(GiftResaleBox, strong, peer, std::move(info)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -110,6 +110,7 @@ struct StarGiftUpgradeArgs {
|
||||
Fn<void(bool)> ready;
|
||||
not_null<PeerData*> peer;
|
||||
Data::SavedStarGiftId savedId;
|
||||
QString giftPrepayUpgradeHash;
|
||||
int cost = 0;
|
||||
bool canAddSender = false;
|
||||
bool canAddComment = false;
|
||||
@@ -157,4 +158,11 @@ void ShowResaleGiftBoughtToast(
|
||||
not_null<PeerData*> to,
|
||||
const Data::UniqueGift &gift);
|
||||
|
||||
[[nodiscard]] rpl::lifetime ShowStarGiftResale(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
uint64 giftId,
|
||||
QString title,
|
||||
Fn<void()> finishRequesting);
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -641,20 +641,11 @@ void ChangeSetNameBox(
|
||||
const auto it = sets.find(input.id);
|
||||
return (it == sets.end()) ? QString() : it->second->title;
|
||||
}();
|
||||
const auto wrap = box->addRow(object_ptr<Ui::FixedHeightWidget>(
|
||||
const auto field = box->addRow(object_ptr<Ui::InputField>(
|
||||
box,
|
||||
st::editStickerSetNameField.heightMin));
|
||||
auto owned = object_ptr<Ui::InputField>(
|
||||
wrap,
|
||||
st::editStickerSetNameField,
|
||||
tr::lng_stickers_context_edit_name(),
|
||||
wasName);
|
||||
const auto field = owned.data();
|
||||
wrap->widthValue() | rpl::start_with_next([=](int width) {
|
||||
field->move(0, 0);
|
||||
field->resize(width, field->height());
|
||||
wrap->resize(width, field->height());
|
||||
}, wrap->lifetime());
|
||||
wasName));
|
||||
field->selectAll();
|
||||
constexpr auto kMaxSetNameLength = 50;
|
||||
field->setMaxLength(kMaxSetNameLength);
|
||||
|
||||
@@ -12,8 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_cloud_password.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/passcode_box.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_star_gift.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "data/data_user.h"
|
||||
#include "boxes/filters/edit_filter_chats_list.h" // CreatePe...tionSubtitle.
|
||||
#include "boxes/peer_list_box.h"
|
||||
@@ -673,6 +675,107 @@ void ShowTransferGiftBox(
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
void SetThemeFromUniqueGift(
|
||||
not_null<Window::SessionController*> window,
|
||||
std::shared_ptr<Data::UniqueGift> unique) {
|
||||
class Controller final : public ChooseRecipientBoxController {
|
||||
public:
|
||||
Controller(
|
||||
not_null<Window::SessionController*> window,
|
||||
std::shared_ptr<Data::UniqueGift> unique)
|
||||
: ChooseRecipientBoxController({
|
||||
.session = &window->session(),
|
||||
.callback = [=](not_null<Data::Thread*> thread) {
|
||||
const auto weak = base::make_weak(window);
|
||||
const auto peer = thread->peer();
|
||||
SendPeerThemeChangeRequest(window, peer, QString(), unique);
|
||||
if (weak) window->showPeerHistory(peer);
|
||||
if (weak) window->hideLayer(anim::type::normal);
|
||||
},
|
||||
.filter = [=](not_null<Data::Thread*> thread) {
|
||||
return thread->peer()->isUser();
|
||||
},
|
||||
.moneyRestrictionError = WriteMoneyRestrictionError,
|
||||
}) {
|
||||
}
|
||||
|
||||
private:
|
||||
void prepareViewHook() override {
|
||||
ChooseRecipientBoxController::prepareViewHook();
|
||||
delegate()->peerListSetTitle(tr::lng_gift_transfer_choose());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
window->show(
|
||||
Box<PeerListBox>(
|
||||
std::make_unique<Controller>(window, std::move(unique)),
|
||||
[](not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_cancel(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
void SendPeerThemeChangeRequest(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
const QString &token,
|
||||
const std::shared_ptr<Data::UniqueGift> &unique,
|
||||
bool locallySet) {
|
||||
const auto api = &peer->session().api();
|
||||
|
||||
api->request(MTPmessages_SetChatWallPaper(
|
||||
MTP_flags(0),
|
||||
peer->input,
|
||||
MTPInputWallPaper(),
|
||||
MTPWallPaperSettings(),
|
||||
MTPint()
|
||||
)).afterDelay(10).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result);
|
||||
}).send();
|
||||
|
||||
api->request(MTPmessages_SetChatTheme(
|
||||
peer->input,
|
||||
(unique
|
||||
? MTP_inputChatThemeUniqueGift(MTP_string(unique->slug))
|
||||
: MTP_inputChatTheme(MTP_string(token)))
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result);
|
||||
if (!locallySet) {
|
||||
peer->updateFullForced();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void SetPeerTheme(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
const QString &token,
|
||||
const std::shared_ptr<Ui::ChatTheme> &theme) {
|
||||
const auto giftTheme = token.startsWith(u"gift:"_q)
|
||||
? peer->owner().cloudThemes().themeForToken(token)
|
||||
: std::optional<Data::CloudTheme>();
|
||||
|
||||
peer->setThemeToken(token);
|
||||
const auto dropWallPaper = (peer->wallPaper() != nullptr);
|
||||
if (dropWallPaper) {
|
||||
peer->setWallPaper({});
|
||||
}
|
||||
|
||||
if (theme) {
|
||||
// Remember while changes propagate through event loop.
|
||||
controller->pushLastUsedChatTheme(theme);
|
||||
}
|
||||
|
||||
SendPeerThemeChangeRequest(
|
||||
controller,
|
||||
peer,
|
||||
token,
|
||||
giftTheme ? giftTheme->unique : nullptr,
|
||||
true);
|
||||
}
|
||||
|
||||
void ShowBuyResaleGiftBox(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
|
||||
@@ -20,6 +20,10 @@ struct UniqueGift;
|
||||
class SavedStarGiftId;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
class ChatTheme;
|
||||
} // namespace Ui
|
||||
|
||||
void ShowTransferToBox(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
@@ -45,3 +49,18 @@ bool ShowResaleGiftLater(
|
||||
bool ShowTransferGiftLater(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<Data::UniqueGift> gift);
|
||||
|
||||
void SetThemeFromUniqueGift(
|
||||
not_null<Window::SessionController*> window,
|
||||
std::shared_ptr<Data::UniqueGift> unique);
|
||||
void SendPeerThemeChangeRequest(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
const QString &token,
|
||||
const std::shared_ptr<Data::UniqueGift> &unique,
|
||||
bool locallySet = false);
|
||||
void SetPeerTheme(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
const QString &token,
|
||||
const std::shared_ptr<Ui::ChatTheme> &theme);
|
||||
|
||||
@@ -208,7 +208,7 @@ void TranslateBox(
|
||||
box,
|
||||
CreateLoadingTextWidget(
|
||||
box,
|
||||
st::aboutLabel,
|
||||
st::aboutLabel.style,
|
||||
std::min(original->entity()->height() / lineHeight, kMaxLines),
|
||||
state->to.value() | rpl::map([=](LanguageId id) {
|
||||
return id.locale().textDirection() == Qt::RightToLeft;
|
||||
|
||||
@@ -324,7 +324,7 @@ void UsernamesBox(
|
||||
|
||||
const auto editor = box->addRow(
|
||||
object_ptr<UsernameEditor>(box, peer),
|
||||
{});
|
||||
style::margins());
|
||||
editor->setEnabled(!isBot);
|
||||
box->setFocusCallback([=] { editor->setInnerFocus(); });
|
||||
|
||||
@@ -366,7 +366,7 @@ void UsernamesBox(
|
||||
!isBot
|
||||
? [=] { box->scrollToY(0); editor->setInnerFocus(); }
|
||||
: Fn<void()>(nullptr)),
|
||||
{});
|
||||
style::margins());
|
||||
|
||||
const auto finish = [=] {
|
||||
list->save(
|
||||
|
||||
@@ -825,7 +825,7 @@ void ShowCallsBox(not_null<::Window::SessionController*> window) {
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
box,
|
||||
object_ptr<Ui::VerticalLayout>(box)),
|
||||
{});
|
||||
style::margins());
|
||||
groupCalls->hide(anim::type::instant);
|
||||
groupCalls->toggleOn(state->groupCallsController.shownValue());
|
||||
|
||||
@@ -833,8 +833,7 @@ void ShowCallsBox(not_null<::Window::SessionController*> window) {
|
||||
groupCalls->entity(),
|
||||
tr::lng_call_box_groupcalls_subtitle());
|
||||
state->groupCallsDelegate.setContent(groupCalls->entity()->add(
|
||||
object_ptr<PeerListContent>(box, &state->groupCallsController),
|
||||
{}));
|
||||
object_ptr<PeerListContent>(box, &state->groupCallsController)));
|
||||
state->groupCallsController.setDelegate(&state->groupCallsDelegate);
|
||||
Ui::AddSkip(groupCalls->entity());
|
||||
Ui::AddDivider(groupCalls->entity());
|
||||
@@ -853,7 +852,7 @@ void ShowCallsBox(not_null<::Window::SessionController*> window) {
|
||||
|
||||
const auto content = box->addRow(
|
||||
object_ptr<PeerListContent>(box, &state->callsController),
|
||||
{});
|
||||
style::margins());
|
||||
state->callsDelegate.setContent(content);
|
||||
state->callsController.setDelegate(&state->callsDelegate);
|
||||
|
||||
|
||||
@@ -116,13 +116,12 @@ void ConferenceCallJoinConfirm(
|
||||
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
|
||||
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_confcall_join_title(),
|
||||
st::boxTitle)),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding);
|
||||
tr::lng_confcall_join_title(),
|
||||
st::boxTitle),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding,
|
||||
style::al_top);
|
||||
const auto wrapName = [&](not_null<PeerData*> peer) {
|
||||
return rpl::single(Ui::Text::Bold(peer->shortName()));
|
||||
};
|
||||
@@ -323,13 +322,12 @@ void ShowConferenceCallLinkBox(
|
||||
Info::BotStarRef::CreateLinkHeaderIcon(box, &call->session()),
|
||||
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_confcall_link_title(),
|
||||
st.box ? st.box->title : st::boxTitle)),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding);
|
||||
tr::lng_confcall_link_title(),
|
||||
st.box ? st.box->title : st::boxTitle),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding,
|
||||
style::al_top);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
|
||||
@@ -1042,13 +1042,12 @@ not_null<Ui::RpWidget*> CreateReActivateHeader(not_null<QWidget*> parent) {
|
||||
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
|
||||
|
||||
result->add(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
result,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
result,
|
||||
tr::lng_confcall_inactive_title(),
|
||||
st::boxTitle)),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding);
|
||||
tr::lng_confcall_inactive_title(),
|
||||
st::boxTitle),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding,
|
||||
style::al_top);
|
||||
result->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
result,
|
||||
|
||||
@@ -1295,6 +1295,7 @@ historyRecordLockPosition: point(1px, 22px);
|
||||
historyRecordCancelButtonWidth: 100px;
|
||||
historyRecordCancelButtonFg: lightButtonFg;
|
||||
|
||||
historyRecordTooltipSkip: 8px;
|
||||
historyRecordTooltip: ImportantTooltip(defaultImportantTooltip) {
|
||||
padding: margins(4px, 4px, 4px, 4px);
|
||||
radius: 11px;
|
||||
@@ -1626,4 +1627,17 @@ frozenInfoBox: Box(defaultBox) {
|
||||
shadowIgnoreTopSkip: true;
|
||||
}
|
||||
|
||||
menuTranscribeItemPadding: margins(0px, 10px, 0px, 10px);
|
||||
menuTranscribeDummyButton: IconButton(defaultIconButton) {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
icon: icon {{ "chat/input_attach", historyComposeIconFgOver }};
|
||||
iconOver: icon {{ "chat/input_attach", historyComposeIconFgOver }};
|
||||
|
||||
rippleAreaPosition: point(3px, 3px);
|
||||
rippleAreaSize: 34px;
|
||||
ripple: defaultRippleAnimationBgOver;
|
||||
}
|
||||
|
||||
roundVideoFont: font(14px semibold);
|
||||
|
||||
@@ -656,8 +656,20 @@ bool Application::eventFilter(QObject *object, QEvent *e) {
|
||||
updateNonIdle();
|
||||
} break;
|
||||
|
||||
case QEvent::KeyRelease: {
|
||||
const auto event = static_cast<QKeyEvent*>(e);
|
||||
if (Shortcuts::HandlePossibleChatSwitch(event)) {
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
|
||||
case QEvent::ShortcutOverride: {
|
||||
// handle shortcuts ourselves
|
||||
// Ctrl+Tab/Ctrl+Shift+Tab chat switch is a special shortcut case,
|
||||
// because it not only does an action on the shortcut activation,
|
||||
// but also keeps the UI visible until you release the Ctrl key.
|
||||
Shortcuts::HandlePossibleChatSwitch(static_cast<QKeyEvent*>(e));
|
||||
|
||||
// Handle all the shortcut management manually.
|
||||
return true;
|
||||
} break;
|
||||
|
||||
|
||||
@@ -240,7 +240,8 @@ QByteArray Settings::serialize() const {
|
||||
+ Serialize::stringSize(_customFontFamily)
|
||||
+ sizeof(qint32) * 3
|
||||
+ Serialize::bytearraySize(_tonsiteStorageToken)
|
||||
+ sizeof(qint32) * 8;
|
||||
+ sizeof(qint32) * 8
|
||||
+ sizeof(ushort);
|
||||
|
||||
auto result = QByteArray();
|
||||
result.reserve(size);
|
||||
@@ -402,7 +403,8 @@ QByteArray Settings::serialize() const {
|
||||
<< SerializeVideoQuality(_videoQuality)
|
||||
<< qint32(_ivZoom.current())
|
||||
<< qint32(_systemDarkModeEnabled.current() ? 1 : 0)
|
||||
<< qint32(_quickDialogAction);
|
||||
<< qint32(_quickDialogAction)
|
||||
<< _notificationsVolume;
|
||||
}
|
||||
|
||||
Ensures(result.size() == size);
|
||||
@@ -532,6 +534,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
quint32 videoQuality = SerializeVideoQuality(_videoQuality);
|
||||
quint32 chatFiltersHorizontal = _chatFiltersHorizontal.current() ? 1 : 0;
|
||||
quint32 quickDialogAction = quint32(_quickDialogAction);
|
||||
ushort notificationsVolume = _notificationsVolume;
|
||||
|
||||
stream >> themesAccentColors;
|
||||
if (!stream.atEnd()) {
|
||||
@@ -863,6 +866,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
if (!stream.atEnd()) {
|
||||
stream >> quickDialogAction;
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
stream >> notificationsVolume;
|
||||
}
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("App Error: "
|
||||
"Bad data for Core::Settings::constructFromSerialized()"));
|
||||
@@ -1085,6 +1091,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
_videoQuality = DeserializeVideoQuality(videoQuality);
|
||||
_chatFiltersHorizontal = (chatFiltersHorizontal == 1);
|
||||
_quickDialogAction = Dialogs::Ui::QuickDialogAction(quickDialogAction);
|
||||
_notificationsVolume = notificationsVolume;
|
||||
}
|
||||
|
||||
QString Settings::getSoundPath(const QString &key) const {
|
||||
@@ -1477,6 +1484,7 @@ void Settings::resetOnLastLogout() {
|
||||
_videoQuality = {};
|
||||
_chatFiltersHorizontal = false;
|
||||
_quickDialogAction = Dialogs::Ui::QuickDialogAction::Disabled;
|
||||
_notificationsVolume = 100;
|
||||
|
||||
_recentEmojiPreload.clear();
|
||||
_recentEmoji.clear();
|
||||
|
||||
@@ -950,6 +950,13 @@ public:
|
||||
[[nodiscard]] Dialogs::Ui::QuickDialogAction quickDialogAction() const;
|
||||
void setQuickDialogAction(Dialogs::Ui::QuickDialogAction);
|
||||
|
||||
[[nodiscard]] ushort notificationsVolume() const {
|
||||
return _notificationsVolume;
|
||||
}
|
||||
void setNotificationsVolume(ushort value) {
|
||||
_notificationsVolume = value;
|
||||
}
|
||||
|
||||
void resetOnLastLogout();
|
||||
|
||||
private:
|
||||
@@ -1093,6 +1100,8 @@ private:
|
||||
Dialogs::Ui::QuickDialogAction _quickDialogAction
|
||||
= Dialogs::Ui::QuickDialogAction::Disabled;
|
||||
|
||||
ushort _notificationsVolume = 100;
|
||||
|
||||
QByteArray _photoEditorBrush;
|
||||
|
||||
};
|
||||
|
||||
@@ -646,7 +646,9 @@ bool ResolveUsernameOrPhone(
|
||||
const auto threadId = topicId ? topicId : threadParam.toInt();
|
||||
const auto gameParam = params.value(u"game"_q);
|
||||
const auto videot = params.value(u"t"_q);
|
||||
|
||||
if (params.contains(u"direct"_q)) {
|
||||
resolveType = ResolveType::ChannelDirect;
|
||||
}
|
||||
if (!gameParam.isEmpty() && validDomain(gameParam)) {
|
||||
startToken = gameParam;
|
||||
resolveType = ResolveType::ShareGame;
|
||||
@@ -657,11 +659,7 @@ bool ResolveUsernameOrPhone(
|
||||
startToken = params.value(u"startapp"_q);
|
||||
}
|
||||
}
|
||||
const auto historyInNewWindow =
|
||||
(params.value(u"tdesktop_target"_q) == u"blank"_q);
|
||||
if (!historyInNewWindow) {
|
||||
controller->window().activate();
|
||||
}
|
||||
controller->window().activate();
|
||||
controller->showPeerByLink(Window::PeerByLinkInfo{
|
||||
.usernameOrId = domain,
|
||||
.phone = phone,
|
||||
@@ -712,7 +710,8 @@ bool ResolveUsernameOrPhone(
|
||||
: std::nullopt),
|
||||
.clickFromMessageId = myContext.itemId,
|
||||
.clickFromBotWebviewContext = myContext.botWebviewContext,
|
||||
.historyInNewWindow = historyInNewWindow,
|
||||
.historyInNewWindow =
|
||||
(params.value(u"tdesktop_target"_q) == u"blank"_q),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@@ -1092,7 +1091,17 @@ bool ShowCollectibleUsername(
|
||||
}
|
||||
const auto username = match->captured(1);
|
||||
const auto peerId = PeerId(match->captured(2).toULongLong());
|
||||
controller->resolveCollectible(peerId, username);
|
||||
const auto weak = base::make_weak(controller);
|
||||
controller->resolveCollectible(peerId, username, [=](const QString &e) {
|
||||
if (e == u"COLLECTIBLE_NOT_FOUND"_q) {
|
||||
if (const auto strong = weak.get()) {
|
||||
TextUtilities::SetClipboardText({
|
||||
strong->session().createInternalLinkFull(username)
|
||||
});
|
||||
strong->showToast(tr::lng_username_copied(tr::now));
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1311,8 +1320,9 @@ bool ResolveTestChatTheme(
|
||||
qthelp::UrlParamNameTransform::ToLower);
|
||||
if (const auto history = controller->activeChatCurrent().history()) {
|
||||
controller->clearCachedChatThemes();
|
||||
const auto theme = history->owner().cloudThemes().updateThemeFromLink(
|
||||
history->peer->themeEmoji(),
|
||||
const auto owner = &history->owner();
|
||||
const auto theme = owner->cloudThemes().updateThemeFromLink(
|
||||
history->peer->themeToken(),
|
||||
params);
|
||||
if (theme) {
|
||||
if (!params["export"].isEmpty()) {
|
||||
@@ -1920,8 +1930,6 @@ bool InternalPassportLink(const QString &url) {
|
||||
bool StartUrlRequiresActivate(const QString &url) {
|
||||
return Core::App().passcodeLocked()
|
||||
? true
|
||||
: url.contains(u"tdesktop_target"_q)
|
||||
? false
|
||||
: !InternalPassportLink(url);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@ constexpr auto kCountLimit = 256; // How many shortcuts can be in json file.
|
||||
rpl::event_stream<not_null<Request*>> RequestsStream;
|
||||
bool Paused/* = false*/;
|
||||
|
||||
Qt::Key ChatSwitchModifier/* = Qt::Key()*/;
|
||||
bool ChatSwitchStarted/* = false*/;
|
||||
rpl::event_stream<ChatSwitchRequest> ChatSwitchStream;
|
||||
|
||||
const auto AutoRepeatCommands = base::flat_set<Command>{
|
||||
Command::MediaPrevious,
|
||||
Command::MediaNext,
|
||||
@@ -112,6 +116,8 @@ const auto CommandByName = base::flat_map<QString, Command>{
|
||||
{ u"show_chat_menu"_q , Command::ShowChatMenu },
|
||||
{ u"show_chat_preview"_q , Command::ShowChatPreview },
|
||||
|
||||
{ u"record_voice"_q , Command::RecordVoice },
|
||||
|
||||
// Shortcuts that have no default values.
|
||||
{ u"message"_q , Command::JustSendMessage },
|
||||
{ u"message_silently"_q , Command::SendSilentMessage },
|
||||
@@ -119,6 +125,7 @@ const auto CommandByName = base::flat_map<QString, Command>{
|
||||
{ u"media_viewer_video_fullscreen"_q , Command::MediaViewerFullscreen },
|
||||
{ u"show_scheduled"_q , Command::ShowScheduled },
|
||||
{ u"archive_chat"_q , Command::ArchiveChat },
|
||||
{ u"record_round"_q , Command::RecordRound },
|
||||
//
|
||||
};
|
||||
|
||||
@@ -140,6 +147,7 @@ const base::flat_map<Command, QString> &CommandNames() {
|
||||
Command::MediaViewerFullscreen,
|
||||
Command::ShowScheduled,
|
||||
Command::ArchiveChat,
|
||||
Command::RecordRound,
|
||||
};
|
||||
|
||||
class Manager {
|
||||
@@ -152,6 +160,7 @@ public:
|
||||
void toggleMedia(bool toggled);
|
||||
void toggleSupport(bool toggled);
|
||||
void listen(not_null<QWidget*> widget);
|
||||
[[nodiscard]] bool handles(const QKeySequence &sequence) const;
|
||||
|
||||
[[nodiscard]] const QStringList &errors() const;
|
||||
|
||||
@@ -357,6 +366,10 @@ void Manager::listen(not_null<QWidget*> widget) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Manager::handles(const QKeySequence &sequence) const {
|
||||
return _shortcuts.contains(sequence);
|
||||
}
|
||||
|
||||
void Manager::pruneListened() {
|
||||
for (auto i = begin(_listened); i != end(_listened);) {
|
||||
if (i->data()) {
|
||||
@@ -466,10 +479,6 @@ void Manager::fillDefaults() {
|
||||
set(u"ctrl+pgup"_q, Command::ChatPrevious);
|
||||
set(u"alt+up"_q, Command::ChatPrevious);
|
||||
|
||||
set(u"%1+tab"_q.arg(ctrl), Command::ChatNext);
|
||||
set(u"%1+shift+tab"_q.arg(ctrl), Command::ChatPrevious);
|
||||
set(u"%1+backtab"_q.arg(ctrl), Command::ChatPrevious);
|
||||
|
||||
set(u"ctrl+alt+home"_q, Command::ChatFirst);
|
||||
set(u"ctrl+alt+end"_q, Command::ChatLast);
|
||||
|
||||
@@ -509,6 +518,8 @@ void Manager::fillDefaults() {
|
||||
set(u"ctrl+\\"_q, Command::ShowChatMenu);
|
||||
set(u"ctrl+]"_q, Command::ShowChatPreview);
|
||||
|
||||
set(u"ctrl+r"_q, Command::RecordVoice);
|
||||
|
||||
_defaults = keysCurrents();
|
||||
}
|
||||
|
||||
@@ -794,6 +805,72 @@ bool HandleEvent(
|
||||
return Launch(Data.lookup(object));
|
||||
}
|
||||
|
||||
void CancelChatSwitch(Qt::Key result) {
|
||||
ChatSwitchModifier = Qt::Key();
|
||||
if (ChatSwitchStarted) {
|
||||
ChatSwitchStarted = false;
|
||||
ChatSwitchStream.fire({ .action = result });
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<ChatSwitchRequest> ChatSwitchRequests() {
|
||||
return ChatSwitchStream.events();
|
||||
}
|
||||
|
||||
bool HandlePossibleChatSwitch(not_null<QKeyEvent*> event) {
|
||||
const auto type = event->type();
|
||||
if (Paused) {
|
||||
return false;
|
||||
} else if (type == QEvent::ShortcutOverride) {
|
||||
const auto key = Qt::Key(event->key());
|
||||
if (key == Qt::Key_Escape) {
|
||||
CancelChatSwitch(Qt::Key_Escape);
|
||||
return false;
|
||||
} else if (key == Qt::Key_Return || key == Qt::Key_Enter) {
|
||||
CancelChatSwitch(Qt::Key_Enter);
|
||||
return false;
|
||||
}
|
||||
const auto ctrl = Platform::IsMac()
|
||||
? Qt::MetaModifier
|
||||
: Qt::ControlModifier;
|
||||
|
||||
if (Data.handles(ctrl | Qt::ShiftModifier | Qt::Key_Tab)
|
||||
&& Data.handles(QKeySequence(ctrl | Qt::Key_Tab))
|
||||
&& Data.handles(QKeySequence(ctrl | Qt::Key_Backtab))) {
|
||||
return false;
|
||||
} else if (key == Qt::Key_Control || key == Qt::Key_Meta) {
|
||||
ChatSwitchModifier = key;
|
||||
} else if (key == Qt::Key_Tab || key == Qt::Key_Backtab) {
|
||||
const auto modifiers = event->modifiers();
|
||||
if (modifiers & ctrl) {
|
||||
if (Data.handles(modifiers | key)) {
|
||||
return false;
|
||||
}
|
||||
if (ChatSwitchModifier == Qt::Key()) {
|
||||
ChatSwitchModifier = Platform::IsMac()
|
||||
? Qt::Key_Meta
|
||||
: Qt::Key_Control;
|
||||
}
|
||||
const auto action = (modifiers & Qt::ShiftModifier)
|
||||
? Qt::Key_Backtab
|
||||
: key;
|
||||
const auto started = !std::exchange(ChatSwitchStarted, true);
|
||||
ChatSwitchStream.fire({
|
||||
.action = action,
|
||||
.started = started,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (type == QEvent::KeyRelease) {
|
||||
const auto key = Qt::Key(event->key());
|
||||
if (key == ChatSwitchModifier) {
|
||||
CancelChatSwitch(Qt::Key_Enter);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ToggleMediaShortcuts(bool toggled) {
|
||||
Data.toggleMedia(toggled);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class QKeyEvent;
|
||||
class QShortcutEvent;
|
||||
|
||||
namespace Shortcuts {
|
||||
|
||||
enum class Command {
|
||||
@@ -66,6 +69,9 @@ enum class Command {
|
||||
SendSilentMessage,
|
||||
ScheduleMessage,
|
||||
|
||||
RecordVoice,
|
||||
RecordRound,
|
||||
|
||||
ReadChat,
|
||||
ArchiveChat,
|
||||
|
||||
@@ -119,7 +125,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
rpl::producer<not_null<Request*>> Requests();
|
||||
[[nodiscard]] rpl::producer<not_null<Request*>> Requests();
|
||||
|
||||
void Start();
|
||||
void Finish();
|
||||
@@ -129,7 +135,15 @@ void Listen(not_null<QWidget*> widget);
|
||||
bool Launch(Command command);
|
||||
bool HandleEvent(not_null<QObject*> object, not_null<QShortcutEvent*> event);
|
||||
|
||||
const QStringList &Errors();
|
||||
bool HandlePossibleChatSwitch(not_null<QKeyEvent*> event);
|
||||
|
||||
struct ChatSwitchRequest {
|
||||
Qt::Key action = Qt::Key_Tab; // Key_Tab, Key_Backtab or Key_Escape.
|
||||
bool started = false;
|
||||
};
|
||||
[[nodiscard]] rpl::producer<ChatSwitchRequest> ChatSwitchRequests();
|
||||
|
||||
[[nodiscard]] const QStringList &Errors();
|
||||
|
||||
// Media shortcuts are not enabled by default, because other
|
||||
// applications also use them. They are enabled only when
|
||||
|
||||
@@ -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 = 6000002;
|
||||
constexpr auto AppVersionStr = "6.0.2";
|
||||
constexpr auto AppVersion = 6001000;
|
||||
constexpr auto AppVersionStr = "6.1";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
@@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/components/recent_peers.h"
|
||||
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history.h"
|
||||
#include "main/main_session.h"
|
||||
#include "storage/serialize_common.h"
|
||||
#include "storage/serialize_peer.h"
|
||||
@@ -16,6 +19,7 @@ namespace Data {
|
||||
namespace {
|
||||
|
||||
constexpr auto kLimit = 48;
|
||||
constexpr auto kMaxRememberedOpenChats = 32;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -133,4 +137,40 @@ void RecentPeers::applyLocal(QByteArray serialized) {
|
||||
("Suggestions: RecentPeers read OK, count: %1").arg(_list.size()));
|
||||
}
|
||||
|
||||
std::vector<not_null<Thread*>> RecentPeers::collectChatOpenHistory() const {
|
||||
_session->local().readSearchSuggestions();
|
||||
|
||||
auto result = _opens;
|
||||
result.reserve(result.size() + _list.size());
|
||||
for (const auto &peer : _list) {
|
||||
const auto history = peer->owner().history(peer);
|
||||
const auto thread = not_null<Data::Thread*>(history);
|
||||
if (!ranges::contains(result, thread)) {
|
||||
result.push_back(thread);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void RecentPeers::chatOpenPush(not_null<Thread*> thread) {
|
||||
const auto i = ranges::find(_opens, thread);
|
||||
if (i == end(_opens)) {
|
||||
while (_opens.size() >= kMaxRememberedOpenChats) {
|
||||
_opens.pop_back();
|
||||
}
|
||||
_opens.insert(begin(_opens), thread);
|
||||
} else if (i != begin(_opens)) {
|
||||
ranges::rotate(begin(_opens), i, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void RecentPeers::chatOpenDestroyed(not_null<Thread*> thread) {
|
||||
_opens.erase(ranges::remove(_opens, thread), end(_opens));
|
||||
}
|
||||
|
||||
void RecentPeers::chatOpenKeepUserpics(
|
||||
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> userpics) {
|
||||
_chatOpenUserpicsCache = std::move(userpics);
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
@@ -7,12 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/userpic_view.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Data {
|
||||
|
||||
class Thread;
|
||||
|
||||
class RecentPeers final {
|
||||
public:
|
||||
explicit RecentPeers(not_null<Main::Session*> session);
|
||||
@@ -28,10 +32,22 @@ public:
|
||||
[[nodiscard]] QByteArray serialize() const;
|
||||
void applyLocal(QByteArray serialized);
|
||||
|
||||
[[nodiscard]] auto collectChatOpenHistory() const
|
||||
-> std::vector<not_null<Thread*>>;
|
||||
void chatOpenPush(not_null<Thread*> thread);
|
||||
void chatOpenDestroyed(not_null<Thread*> thread);
|
||||
void chatOpenKeepUserpics(
|
||||
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> userpics);
|
||||
|
||||
private:
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
std::vector<not_null<PeerData*>> _list;
|
||||
std::vector<not_null<Thread*>> _opens;
|
||||
base::flat_map<
|
||||
not_null<PeerData*>,
|
||||
Ui::PeerUserpicView> _chatOpenUserpicsCache;
|
||||
|
||||
rpl::event_stream<> _updates;
|
||||
|
||||
};
|
||||
|
||||
20
Telegram/SourceFiles/data/data_authorization.h
Normal file
20
Telegram/SourceFiles/data/data_authorization.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
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 UnreviewedAuth {
|
||||
uint64 hash = 0;
|
||||
bool unconfirmed = false;
|
||||
TimeId date = 0;
|
||||
QString device;
|
||||
QString location;
|
||||
};
|
||||
|
||||
} // namespace Data
|
||||
@@ -66,7 +66,7 @@ struct PeerUpdate {
|
||||
Notifications = (1ULL << 4),
|
||||
Migration = (1ULL << 5),
|
||||
UnavailableReason = (1ULL << 6),
|
||||
ChatThemeEmoji = (1ULL << 7),
|
||||
ChatThemeToken = (1ULL << 7),
|
||||
ChatWallPaper = (1ULL << 8),
|
||||
IsBlocked = (1ULL << 9),
|
||||
MessagesTTL = (1ULL << 10),
|
||||
|
||||
@@ -235,7 +235,8 @@ void ChannelData::setFlags(ChannelDataFlags which) {
|
||||
| Flag::CallNotEmpty
|
||||
| Flag::SimilarExpanded
|
||||
| Flag::Signatures
|
||||
| Flag::SignatureProfiles)) {
|
||||
| Flag::SignatureProfiles
|
||||
| Flag::ForumTabs)) {
|
||||
if (const auto history = this->owner().historyLoaded(this)) {
|
||||
if (diff & Flag::CallNotEmpty) {
|
||||
history->updateChatListEntry();
|
||||
@@ -262,6 +263,9 @@ void ChannelData::setFlags(ChannelDataFlags which) {
|
||||
if (diff & (Flag::Signatures | Flag::SignatureProfiles)) {
|
||||
session().changes().peerUpdated(this, UpdateFlag::Rights);
|
||||
}
|
||||
if (diff & Flag::ForumTabs) {
|
||||
history->forumTabsChanged(which & Flag::ForumTabs);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (const auto raw = takenForum.get()) {
|
||||
@@ -1402,7 +1406,7 @@ void ApplyChannelUpdate(
|
||||
update.vboosts_applied().value_or_empty(),
|
||||
update.vboosts_unrestrict().value_or_empty());
|
||||
}
|
||||
channel->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
|
||||
channel->setThemeToken(qs(update.vtheme_emoticon().value_or_empty()));
|
||||
channel->setTranslationDisabled(update.is_translations_disabled());
|
||||
|
||||
const auto reactionsLimit = update.vreactions_limit().value_or_empty();
|
||||
|
||||
@@ -486,7 +486,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->setThemeToken(qs(update.vtheme_emoticon().value_or_empty()));
|
||||
chat->setTranslationDisabled(update.is_translations_disabled());
|
||||
const auto reactionsLimit = update.vreactions_limit().value_or_empty();
|
||||
if (const auto allowed = update.vavailable_reactions()) {
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_cloud_themes.h"
|
||||
|
||||
#include "api/api_premium.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/themes/window_theme_preview.h"
|
||||
#include "window/themes/window_theme_editor_box.h"
|
||||
@@ -26,6 +27,7 @@ namespace {
|
||||
|
||||
constexpr auto kFirstReloadTimeout = 10 * crl::time(1000);
|
||||
constexpr auto kReloadTimeout = 3600 * crl::time(1000);
|
||||
constexpr auto kGiftThemesLimit = 24;
|
||||
|
||||
bool IsTestingColors/* = false*/;
|
||||
|
||||
@@ -37,44 +39,38 @@ CloudTheme CloudTheme::Parse(
|
||||
bool parseSettings) {
|
||||
const auto document = data.vdocument();
|
||||
const auto paper = [&](const MTPThemeSettings &settings) {
|
||||
return settings.match([&](const MTPDthemeSettings &data) {
|
||||
return data.vwallpaper()
|
||||
? WallPaper::Create(session, *data.vwallpaper())
|
||||
: std::nullopt;
|
||||
});
|
||||
const auto &data = settings.data();
|
||||
return data.vwallpaper()
|
||||
? WallPaper::Create(session, *data.vwallpaper())
|
||||
: std::nullopt;
|
||||
};
|
||||
const auto outgoingMessagesColors = [&](
|
||||
const MTPThemeSettings &settings) {
|
||||
auto result = std::vector<QColor>();
|
||||
settings.match([&](const MTPDthemeSettings &data) {
|
||||
if (const auto colors = data.vmessage_colors()) {
|
||||
for (const auto &color : colors->v) {
|
||||
result.push_back(Ui::ColorFromSerialized(color));
|
||||
}
|
||||
const auto &data = settings.data();
|
||||
if (const auto colors = data.vmessage_colors()) {
|
||||
for (const auto &color : colors->v) {
|
||||
result.push_back(Ui::ColorFromSerialized(color));
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const auto accentColor = [&](const MTPThemeSettings &settings) {
|
||||
return settings.match([&](const MTPDthemeSettings &data) {
|
||||
return Ui::ColorFromSerialized(data.vaccent_color());
|
||||
});
|
||||
const auto &data = settings.data();
|
||||
return Ui::ColorFromSerialized(data.vaccent_color());
|
||||
};
|
||||
const auto outgoingAccentColor = [&](const MTPThemeSettings &settings) {
|
||||
return settings.match([&](const MTPDthemeSettings &data) {
|
||||
return Ui::MaybeColorFromSerialized(data.voutbox_accent_color());
|
||||
});
|
||||
const auto &data = settings.data();
|
||||
return Ui::MaybeColorFromSerialized(data.voutbox_accent_color());
|
||||
};
|
||||
const auto basedOnDark = [&](const MTPThemeSettings &settings) {
|
||||
return settings.match([&](const MTPDthemeSettings &data) {
|
||||
return data.vbase_theme().match([](
|
||||
const MTPDbaseThemeNight &) {
|
||||
return true;
|
||||
}, [](const MTPDbaseThemeTinted &) {
|
||||
return true;
|
||||
}, [](const auto &) {
|
||||
return false;
|
||||
});
|
||||
const auto &data = settings.data();
|
||||
return data.vbase_theme().match([](const MTPDbaseThemeNight &) {
|
||||
return true;
|
||||
}, [](const MTPDbaseThemeTinted &) {
|
||||
return true;
|
||||
}, [](const auto &) {
|
||||
return false;
|
||||
});
|
||||
};
|
||||
const auto settings = [&] {
|
||||
@@ -111,6 +107,80 @@ CloudTheme CloudTheme::Parse(
|
||||
};
|
||||
}
|
||||
|
||||
CloudTheme CloudTheme::Parse(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDchatThemeUniqueGift &data,
|
||||
bool parseSettings) {
|
||||
const auto gift = Api::FromTL(session, data.vgift());
|
||||
if (!gift || !gift->unique) {
|
||||
return {};
|
||||
}
|
||||
const auto paper = [&](const MTPThemeSettings &settings) {
|
||||
const auto &data = settings.data();
|
||||
return data.vwallpaper()
|
||||
? WallPaper::Create(session, *data.vwallpaper())
|
||||
: std::nullopt;
|
||||
};
|
||||
const auto basedOnDark = [&](const MTPThemeSettings &settings) {
|
||||
const auto &data = settings.data();
|
||||
return data.vbase_theme().match([](const MTPDbaseThemeNight &) {
|
||||
return true;
|
||||
}, [](const MTPDbaseThemeTinted &) {
|
||||
return true;
|
||||
}, [](const auto &) {
|
||||
return false;
|
||||
});
|
||||
};
|
||||
const auto outgoingMessagesColors = [&](
|
||||
const MTPThemeSettings &settings) {
|
||||
auto result = std::vector<QColor>();
|
||||
const auto &data = settings.data();
|
||||
if (const auto colors = data.vmessage_colors()) {
|
||||
for (const auto &color : colors->v) {
|
||||
result.push_back(Ui::ColorFromSerialized(color));
|
||||
}
|
||||
//} else if (basedOnDark(settings)) {
|
||||
// result.push_back(gift->unique->backdrop.edgeColor);
|
||||
//} else {
|
||||
// result.push_back(anim::color(
|
||||
// gift->unique->backdrop.patternColor,
|
||||
// QColor(255, 255, 255, 255),
|
||||
// 0.75));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const auto accentColor = [&](const MTPThemeSettings &settings) {
|
||||
const auto &data = settings.data();
|
||||
return Ui::ColorFromSerialized(data.vaccent_color());
|
||||
};
|
||||
const auto outgoingAccentColor = [&](const MTPThemeSettings &settings) {
|
||||
const auto &data = settings.data();
|
||||
return Ui::MaybeColorFromSerialized(
|
||||
data.voutbox_accent_color()
|
||||
);// .value_or(gift->unique->backdrop.patternColor);
|
||||
};
|
||||
const auto settings = [&] {
|
||||
auto result = base::flat_map<Type, Settings>();
|
||||
for (const auto &fields : data.vtheme_settings().v) {
|
||||
const auto type = basedOnDark(fields) ? Type::Dark : Type::Light;
|
||||
result.emplace(type, Settings{
|
||||
.paper = paper(fields),
|
||||
.accentColor = accentColor(fields),
|
||||
.outgoingAccentColor = outgoingAccentColor(fields),
|
||||
.outgoingMessagesColors = outgoingMessagesColors(fields),
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return {
|
||||
.id = gift->unique->id,
|
||||
.unique = gift->unique,
|
||||
.settings = (parseSettings
|
||||
? settings()
|
||||
: base::flat_map<Type, Settings>()),
|
||||
};
|
||||
}
|
||||
|
||||
CloudTheme CloudTheme::Parse(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPTheme &data,
|
||||
@@ -382,9 +452,16 @@ rpl::producer<> CloudThemes::chatThemesUpdated() const {
|
||||
return _chatThemesUpdates.events();
|
||||
}
|
||||
|
||||
std::optional<CloudTheme> CloudThemes::themeForEmoji(
|
||||
const QString &emoticon) const {
|
||||
const auto emoji = Ui::Emoji::Find(emoticon);
|
||||
std::optional<CloudTheme> CloudThemes::themeForToken(
|
||||
const QString &token) const {
|
||||
if (token.startsWith(u"gift:"_q)) {
|
||||
const auto id = QStringView(token).mid(5).toULongLong();
|
||||
const auto i = _giftThemes.find(id);
|
||||
return (i != end(_giftThemes))
|
||||
? i->second
|
||||
: std::optional<CloudTheme>();
|
||||
}
|
||||
const auto emoji = Ui::Emoji::Find(token);
|
||||
if (!emoji) {
|
||||
return {};
|
||||
}
|
||||
@@ -394,18 +471,21 @@ std::optional<CloudTheme> CloudThemes::themeForEmoji(
|
||||
return (i != end(_chatThemes)) ? std::make_optional(*i) : std::nullopt;
|
||||
}
|
||||
|
||||
rpl::producer<std::optional<CloudTheme>> CloudThemes::themeForEmojiValue(
|
||||
const QString &emoticon) {
|
||||
rpl::producer<std::optional<CloudTheme>> CloudThemes::themeForTokenValue(
|
||||
const QString &token) {
|
||||
if (token.startsWith(u"gift:"_q)) {
|
||||
return rpl::single(themeForToken(token));
|
||||
}
|
||||
const auto testing = TestingColors();
|
||||
if (!Ui::Emoji::Find(emoticon)) {
|
||||
if (!Ui::Emoji::Find(token)) {
|
||||
return rpl::single<std::optional<CloudTheme>>(std::nullopt);
|
||||
} else if (auto result = themeForEmoji(emoticon)) {
|
||||
} else if (auto result = themeForToken(token)) {
|
||||
if (testing) {
|
||||
return rpl::single(
|
||||
std::move(result)
|
||||
) | rpl::then(chatThemesUpdated(
|
||||
) | rpl::map([=] {
|
||||
return themeForEmoji(emoticon);
|
||||
return themeForToken(token);
|
||||
}) | rpl::filter([](const std::optional<CloudTheme> &theme) {
|
||||
return theme.has_value();
|
||||
}));
|
||||
@@ -418,12 +498,88 @@ rpl::producer<std::optional<CloudTheme>> CloudThemes::themeForEmojiValue(
|
||||
std::nullopt
|
||||
) | rpl::then(chatThemesUpdated(
|
||||
) | rpl::map([=] {
|
||||
return themeForEmoji(emoticon);
|
||||
return themeForToken(token);
|
||||
}) | rpl::filter([](const std::optional<CloudTheme> &theme) {
|
||||
return theme.has_value();
|
||||
}) | rpl::take(limit));
|
||||
}
|
||||
|
||||
void CloudThemes::myGiftThemesLoadMore(bool reload) {
|
||||
if (reload && !_myGiftThemesTokens.empty()) {
|
||||
_session->api().request(base::take(_myGiftThemesRequestId)).cancel();
|
||||
}
|
||||
if (_myGiftThemesRequestId || (!reload && _myGiftThemesLoaded)) {
|
||||
return;
|
||||
}
|
||||
_myGiftThemesRequestId = _session->api().request(
|
||||
MTPaccount_GetUniqueGiftChatThemes(
|
||||
MTP_int(reload ? 0 : _myGiftThemesTokens.size()),
|
||||
MTP_int(kGiftThemesLimit),
|
||||
MTP_long(_myGiftThemesHash))
|
||||
).done([=](const MTPaccount_ChatThemes &result) {
|
||||
_myGiftThemesRequestId = 0;
|
||||
result.match([&](const MTPDaccount_chatThemes &data) {
|
||||
if (reload || _myGiftThemesTokens.empty()) {
|
||||
_myGiftThemesHash = data.vhash().v;
|
||||
_myGiftThemesTokens.clear();
|
||||
_myGiftThemesLoaded = false;
|
||||
}
|
||||
_session->data().processUsers(data.vusers());
|
||||
_session->data().processChats(data.vchats());
|
||||
const auto &list = data.vthemes().v;
|
||||
const auto got = int(list.size());
|
||||
_myGiftThemesTokens.reserve(_myGiftThemesTokens.size() + got);
|
||||
for (const auto &theme : list) {
|
||||
theme.match([](const MTPDchatTheme &) {
|
||||
}, [&](const MTPDchatThemeUniqueGift &data) {
|
||||
_myGiftThemesTokens.push_back(
|
||||
processGiftThemeGetToken(data));
|
||||
});
|
||||
}
|
||||
_myGiftThemesLoaded = (got < kGiftThemesLimit);
|
||||
_myGiftThemesUpdates.fire({});
|
||||
}, [&](const MTPDaccount_chatThemesNotModified &) {
|
||||
if (!reload) {
|
||||
_myGiftThemesLoaded = true;
|
||||
_myGiftThemesUpdates.fire({});
|
||||
}
|
||||
});
|
||||
}).fail([=] {
|
||||
_myGiftThemesRequestId = 0;
|
||||
_myGiftThemesLoaded = true;
|
||||
}).send();
|
||||
}
|
||||
|
||||
const std::vector<QString> &CloudThemes::myGiftThemesTokens() const {
|
||||
return _myGiftThemesTokens;
|
||||
}
|
||||
|
||||
bool CloudThemes::myGiftThemesReady() const {
|
||||
return !_myGiftThemesTokens.empty() || _myGiftThemesLoaded;
|
||||
}
|
||||
|
||||
rpl::producer<> CloudThemes::myGiftThemesUpdated() const {
|
||||
return _myGiftThemesUpdates.events();
|
||||
}
|
||||
|
||||
QString CloudThemes::processGiftThemeGetToken(
|
||||
const MTPDchatThemeUniqueGift &data) {
|
||||
auto parsed = CloudTheme::Parse(_session, data, true);
|
||||
if (parsed.unique) {
|
||||
const auto id = parsed.unique->id;
|
||||
_giftThemes[id] = std::move(parsed);
|
||||
return u"gift:%1"_q.arg(id);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void CloudThemes::refreshChatThemesFor(const QString &token) {
|
||||
if (token.startsWith(u"gift:"_q)) {
|
||||
return;
|
||||
}
|
||||
refreshChatThemes();
|
||||
}
|
||||
|
||||
bool CloudThemes::TestingColors() {
|
||||
return IsTestingColors;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ class Controller;
|
||||
|
||||
namespace Data {
|
||||
|
||||
struct UniqueGift;
|
||||
class DocumentMedia;
|
||||
|
||||
enum class CloudThemeType {
|
||||
@@ -38,6 +39,7 @@ struct CloudTheme {
|
||||
UserId createdBy = 0;
|
||||
int usersCount = 0;
|
||||
QString emoticon;
|
||||
std::shared_ptr<UniqueGift> unique;
|
||||
|
||||
using Type = CloudThemeType;
|
||||
struct Settings {
|
||||
@@ -52,6 +54,10 @@ struct CloudTheme {
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDtheme &data,
|
||||
bool parseSettings = false);
|
||||
static CloudTheme Parse(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDchatThemeUniqueGift &data,
|
||||
bool parseSettings = false);
|
||||
static CloudTheme Parse(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPTheme &data,
|
||||
@@ -73,9 +79,18 @@ public:
|
||||
void refreshChatThemes();
|
||||
[[nodiscard]] const std::vector<CloudTheme> &chatThemes() const;
|
||||
[[nodiscard]] rpl::producer<> chatThemesUpdated() const;
|
||||
[[nodiscard]] std::optional<CloudTheme> themeForEmoji(
|
||||
const QString &emoticon) const;
|
||||
[[nodiscard]] auto themeForEmojiValue(const QString &emoticon)
|
||||
|
||||
void myGiftThemesLoadMore(bool reload = false);
|
||||
[[nodiscard]] const std::vector<QString> &myGiftThemesTokens() const;
|
||||
[[nodiscard]] rpl::producer<> myGiftThemesUpdated() const;
|
||||
[[nodiscard]] QString processGiftThemeGetToken(
|
||||
const MTPDchatThemeUniqueGift &data);
|
||||
[[nodiscard]] bool myGiftThemesReady() const;
|
||||
|
||||
void refreshChatThemesFor(const QString &token);
|
||||
[[nodiscard]] std::optional<CloudTheme> themeForToken(
|
||||
const QString &token) const;
|
||||
[[nodiscard]] auto themeForTokenValue(const QString &token)
|
||||
-> rpl::producer<std::optional<CloudTheme>>;
|
||||
|
||||
[[nodiscard]] static bool TestingColors();
|
||||
@@ -140,6 +155,13 @@ private:
|
||||
std::vector<CloudTheme> _chatThemes;
|
||||
rpl::event_stream<> _chatThemesUpdates;
|
||||
|
||||
mtpRequestId _myGiftThemesRequestId = 0;
|
||||
base::flat_map<uint64, CloudTheme> _giftThemes;
|
||||
std::vector<QString> _myGiftThemesTokens;
|
||||
rpl::event_stream<> _myGiftThemesUpdates;
|
||||
uint64 _myGiftThemesHash = 0;
|
||||
bool _myGiftThemesLoaded = false;
|
||||
|
||||
base::Timer _reloadCurrentTimer;
|
||||
LoadingDocument _updatingFrom;
|
||||
LoadingDocument _previewFrom;
|
||||
|
||||
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Data {
|
||||
|
||||
struct UniqueGift;
|
||||
struct UniqueGiftValue;
|
||||
|
||||
struct CreditTopupOption final {
|
||||
uint64 credits = 0;
|
||||
@@ -71,8 +72,11 @@ struct CreditsHistoryEntry final {
|
||||
uint64 bareEntryOwnerId = 0;
|
||||
uint64 giftChannelSavedId = 0;
|
||||
uint64 stargiftId = 0;
|
||||
QString giftPrepayUpgradeHash;
|
||||
std::shared_ptr<UniqueGift> uniqueGift;
|
||||
Fn<std::vector<CreditsHistoryEntry>()> pinnedSavedGifts;
|
||||
uint64 nextToUpgradeStickerId = 0;
|
||||
Fn<void()> nextToUpgradeShow;
|
||||
CreditsAmount starrefAmount;
|
||||
int starrefCommission = 0;
|
||||
uint64 starrefRecipientId = 0;
|
||||
|
||||
@@ -1712,6 +1712,10 @@ void DocumentData::forceIsStreamedAnimation() {
|
||||
setMaybeSupportsStreaming(true);
|
||||
}
|
||||
|
||||
bool DocumentData::isMusicForProfile() const {
|
||||
return isSong();
|
||||
}
|
||||
|
||||
bool DocumentData::isVoiceMessage() const {
|
||||
return (type == VoiceDocument);
|
||||
}
|
||||
|
||||
@@ -182,6 +182,7 @@ public:
|
||||
[[nodiscard]] const VideoData *video() const;
|
||||
|
||||
void forceIsStreamedAnimation();
|
||||
[[nodiscard]] bool isMusicForProfile() const;
|
||||
[[nodiscard]] bool isVoiceMessage() const;
|
||||
[[nodiscard]] bool isVideoMessage() const;
|
||||
[[nodiscard]] bool isSong() const;
|
||||
|
||||
@@ -169,7 +169,11 @@ base::binary_guard ReadBackgroundImageAsync(
|
||||
guard = result.make_guard(),
|
||||
callback = std::move(done)
|
||||
]() mutable {
|
||||
auto image = Ui::ReadBackgroundImage(path, bytes, gzipSvg);
|
||||
auto image = Ui::ReadBackgroundImage(path, bytes, gzipSvg).image;
|
||||
if (image.isNull()) {
|
||||
image = QImage(1, 1, QImage::Format_ARGB32_Premultiplied);
|
||||
image.fill(Qt::black);
|
||||
}
|
||||
if (postprocess) {
|
||||
image = postprocess(std::move(image));
|
||||
}
|
||||
|
||||
@@ -204,6 +204,12 @@ struct FileReferenceAccumulator {
|
||||
void push(const MTPstories_Stories &data) {
|
||||
push(data.data().vstories());
|
||||
}
|
||||
void push(const MTPusers_SavedMusic &data) {
|
||||
data.match([&](const MTPDusers_savedMusic &data) {
|
||||
push(data.vdocuments());
|
||||
}, [](const MTPDusers_savedMusicNotModified &data) {
|
||||
});
|
||||
}
|
||||
|
||||
UpdatedFileReferences result;
|
||||
};
|
||||
@@ -273,6 +279,10 @@ UpdatedFileReferences GetFileReferences(const MTPstories_Stories &data) {
|
||||
return GetFileReferencesHelper(data);
|
||||
}
|
||||
|
||||
UpdatedFileReferences GetFileReferences(const MTPusers_SavedMusic &data) {
|
||||
return GetFileReferencesHelper(data);
|
||||
}
|
||||
|
||||
UpdatedFileReferences GetFileReferences(const MTPMessageMedia &data) {
|
||||
return GetFileReferencesHelper(data);
|
||||
}
|
||||
|
||||
@@ -221,6 +221,7 @@ UpdatedFileReferences GetFileReferences(
|
||||
UpdatedFileReferences GetFileReferences(const MTPhelp_PremiumPromo &data);
|
||||
UpdatedFileReferences GetFileReferences(const MTPmessages_WebPage &data);
|
||||
UpdatedFileReferences GetFileReferences(const MTPstories_Stories &data);
|
||||
UpdatedFileReferences GetFileReferences(const MTPusers_SavedMusic &data);
|
||||
|
||||
// Admin Log Event.
|
||||
UpdatedFileReferences GetFileReferences(const MTPMessageMedia &data);
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_forum.h"
|
||||
|
||||
#include "data/components/recent_peers.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_changes.h"
|
||||
@@ -194,6 +195,7 @@ void Forum::applyTopicDeleted(MsgId rootId) {
|
||||
_activeSubsectionTopic = nullptr;
|
||||
}
|
||||
_topicDestroyed.fire(raw);
|
||||
_history->session().recentPeers().chatOpenDestroyed(raw);
|
||||
session().changes().topicUpdated(
|
||||
raw,
|
||||
Data::TopicUpdate::Flag::Destroyed);
|
||||
|
||||
@@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/painter.h"
|
||||
#include "ui/color_int_conversion.h"
|
||||
#include "ui/text/text_custom_emoji.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
@@ -756,6 +757,16 @@ TextWithEntities ForumTopic::titleWithIcon() const {
|
||||
return ForumTopicIconWithTitle(_rootId, _iconId, _title);
|
||||
}
|
||||
|
||||
TextWithEntities ForumTopic::titleWithIconOrLogo() const {
|
||||
if (_iconId || isGeneral()) {
|
||||
return titleWithIcon();
|
||||
}
|
||||
return Ui::Text::SingleCustomEmoji(Data::TopicIconEmojiEntity({
|
||||
.title = _title,
|
||||
.colorId = _colorId,
|
||||
})).append(' ').append(_title);
|
||||
}
|
||||
|
||||
int ForumTopic::titleVersion() const {
|
||||
return _titleVersion;
|
||||
}
|
||||
|
||||
@@ -148,6 +148,7 @@ public:
|
||||
|
||||
[[nodiscard]] QString title() const;
|
||||
[[nodiscard]] TextWithEntities titleWithIcon() const;
|
||||
[[nodiscard]] TextWithEntities titleWithIconOrLogo() const;
|
||||
[[nodiscard]] int titleVersion() const;
|
||||
void applyTitle(const QString &title);
|
||||
[[nodiscard]] DocumentId iconId() const;
|
||||
|
||||
@@ -10,13 +10,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_text_entities.h"
|
||||
#include "data/business/data_shortcut_messages.h"
|
||||
#include "data/components/scheduled_messages.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_saved_music.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "base/random.h"
|
||||
@@ -885,10 +887,10 @@ void Histories::deleteMessagesByDates(
|
||||
}
|
||||
|
||||
void Histories::deleteMessagesByDates(
|
||||
not_null<History*> history,
|
||||
TimeId minDate,
|
||||
TimeId maxDate,
|
||||
bool revoke) {
|
||||
not_null<History*> history,
|
||||
TimeId minDate,
|
||||
TimeId maxDate,
|
||||
bool revoke) {
|
||||
sendRequest(history, RequestType::Delete, [=](Fn<void()> finish) {
|
||||
const auto peer = history->peer;
|
||||
using Flag = MTPmessages_DeleteHistory::Flag;
|
||||
@@ -921,10 +923,14 @@ void Histories::deleteMessages(const MessageIdsList &ids, bool revoke) {
|
||||
base::flat_map<not_null<History*>, QVector<MTPint>> idsByPeer;
|
||||
base::flat_map<not_null<PeerData*>, QVector<MTPint>> scheduledIdsByPeer;
|
||||
base::flat_map<BusinessShortcutId, QVector<MTPint>> quickIdsByShortcut;
|
||||
base::flat_set<not_null<DocumentData*>> savedMusic;
|
||||
for (const auto &itemId : ids) {
|
||||
if (const auto item = _owner->message(itemId)) {
|
||||
const auto history = item->history();
|
||||
if (item->isScheduled()) {
|
||||
if (item->isSavedMusicItem()) {
|
||||
savedMusic.emplace(item->media()->document());
|
||||
continue;
|
||||
} else if (item->isScheduled()) {
|
||||
const auto wasOnServer = !item->isSending()
|
||||
&& !item->hasFailed();
|
||||
auto &scheduled = _owner->session().scheduledMessages();
|
||||
@@ -973,6 +979,9 @@ void Histories::deleteMessages(const MessageIdsList &ids, bool revoke) {
|
||||
api->applyUpdates(result);
|
||||
}).send();
|
||||
}
|
||||
for (const auto &document : savedMusic) {
|
||||
document->owner().savedMusic().remove(document);
|
||||
}
|
||||
|
||||
for (const auto item : remove) {
|
||||
const auto history = item->history();
|
||||
|
||||
@@ -2585,7 +2585,11 @@ std::unique_ptr<HistoryView::Media> MediaGiftBox::createView(
|
||||
not_null<HistoryView::Element*> message,
|
||||
not_null<HistoryItem*> realParent,
|
||||
HistoryView::Element *replacing) {
|
||||
if (const auto &unique = _data.unique) {
|
||||
if (_data.type == GiftType::ChatTheme) {
|
||||
return std::make_unique<HistoryView::ServiceBox>(
|
||||
message,
|
||||
std::make_unique<HistoryView::GiftThemeBox>(message, this));
|
||||
} else if (const auto &unique = _data.unique) {
|
||||
return std::make_unique<HistoryView::MediaGeneric>(
|
||||
message,
|
||||
HistoryView::GenerateUniqueGiftMedia(message, replacing, unique),
|
||||
|
||||
@@ -138,6 +138,7 @@ enum class GiftType : uchar {
|
||||
Credits, // count - credits
|
||||
Ton, // count - nano tons
|
||||
StarGift, // count - stars
|
||||
ChatTheme,
|
||||
};
|
||||
|
||||
struct GiftCode {
|
||||
@@ -150,8 +151,9 @@ struct GiftCode {
|
||||
ChannelData *channel = nullptr;
|
||||
PeerData *channelFrom = nullptr;
|
||||
uint64 channelSavedId = 0;
|
||||
QString giftPrepayUpgradeHash;
|
||||
MsgId giveawayMsgId = 0;
|
||||
MsgId upgradeMsgId = 0;
|
||||
MsgId realGiftMsgId = 0;
|
||||
int starsConverted = 0;
|
||||
int starsToUpgrade = 0;
|
||||
int starsUpgradedBySender = 0;
|
||||
|
||||
@@ -1736,24 +1736,23 @@ PeerId PeerData::groupCallDefaultJoinAs() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PeerData::setThemeEmoji(const QString &emoticon) {
|
||||
if (_themeEmoticon == emoticon) {
|
||||
void PeerData::setThemeToken(const QString &token) {
|
||||
if (_themeToken == token) {
|
||||
return;
|
||||
} else if (!token.startsWith(u"gift:"_q)
|
||||
&& Ui::Emoji::Find(_themeToken) == Ui::Emoji::Find(token)) {
|
||||
_themeToken = token;
|
||||
return;
|
||||
}
|
||||
if (Ui::Emoji::Find(_themeEmoticon) == Ui::Emoji::Find(emoticon)) {
|
||||
_themeEmoticon = emoticon;
|
||||
return;
|
||||
_themeToken = token;
|
||||
if (!token.isEmpty() && !owner().cloudThemes().themeForToken(token)) {
|
||||
owner().cloudThemes().refreshChatThemesFor(token);
|
||||
}
|
||||
_themeEmoticon = emoticon;
|
||||
if (!emoticon.isEmpty()
|
||||
&& !owner().cloudThemes().themeForEmoji(emoticon)) {
|
||||
owner().cloudThemes().refreshChatThemes();
|
||||
}
|
||||
session().changes().peerUpdated(this, UpdateFlag::ChatThemeEmoji);
|
||||
session().changes().peerUpdated(this, UpdateFlag::ChatThemeToken);
|
||||
}
|
||||
|
||||
const QString &PeerData::themeEmoji() const {
|
||||
return _themeEmoticon;
|
||||
const QString &PeerData::themeToken() const {
|
||||
return _themeToken;
|
||||
}
|
||||
|
||||
void PeerData::setWallPaper(
|
||||
|
||||
@@ -530,8 +530,8 @@ public:
|
||||
[[nodiscard]] Data::GroupCall *groupCall() const;
|
||||
[[nodiscard]] PeerId groupCallDefaultJoinAs() const;
|
||||
|
||||
void setThemeEmoji(const QString &emoticon);
|
||||
[[nodiscard]] const QString &themeEmoji() const;
|
||||
void setThemeToken(const QString &token);
|
||||
[[nodiscard]] const QString &themeToken() const;
|
||||
|
||||
void setWallPaper(
|
||||
std::optional<Data::WallPaper> paper,
|
||||
@@ -615,7 +615,7 @@ private:
|
||||
uint8 _userpicHasVideo : 1 = 0;
|
||||
|
||||
QString _about;
|
||||
QString _themeEmoticon;
|
||||
QString _themeToken;
|
||||
std::unique_ptr<Data::WallPaper> _wallPaper;
|
||||
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "core/application.h"
|
||||
#include "data/components/recent_peers.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_histories.h"
|
||||
@@ -452,6 +453,7 @@ void SavedMessages::applySublistDeleted(not_null<PeerData*> sublistPeer) {
|
||||
}
|
||||
|
||||
_sublistDestroyed.fire(raw);
|
||||
_owner->session().recentPeers().chatOpenDestroyed(raw);
|
||||
session().changes().sublistUpdated(
|
||||
raw,
|
||||
Data::SublistUpdate::Flag::Destroyed);
|
||||
|
||||
384
Telegram/SourceFiles/data/data_saved_music.cpp
Normal file
384
Telegram/SourceFiles/data/data_saved_music.cpp
Normal file
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
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_saved_music.h"
|
||||
|
||||
#include "api/api_hash.h"
|
||||
#include "apiwrap.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/history.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/ui_utility.h"
|
||||
|
||||
namespace Data {
|
||||
namespace {
|
||||
|
||||
constexpr auto kPerPage = 50;
|
||||
constexpr auto kReloadIdsEvery = 30 * crl::time(1000);
|
||||
|
||||
[[nodiscard]] not_null<DocumentData*> ItemDocument(
|
||||
not_null<HistoryItem*> item) {
|
||||
return item->media()->document();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SavedMusic::SavedMusic(not_null<Session*> owner)
|
||||
: _owner(owner) {
|
||||
}
|
||||
|
||||
SavedMusic::~SavedMusic() {
|
||||
Expects(_entries.empty());
|
||||
}
|
||||
|
||||
void SavedMusic::clear() {
|
||||
base::take(_entries);
|
||||
}
|
||||
|
||||
bool SavedMusic::Supported(PeerId peerId) {
|
||||
return peerId && peerIsUser(peerId);
|
||||
}
|
||||
|
||||
not_null<HistoryItem*> SavedMusic::musicIdToMsg(
|
||||
PeerId peerId,
|
||||
Entry &entry,
|
||||
not_null<DocumentData*> id) {
|
||||
const auto i = entry.musicIdToMsg.find(id);
|
||||
if (i != end(entry.musicIdToMsg)) {
|
||||
return i->second.get();
|
||||
} else if (!entry.history) {
|
||||
entry.history = _owner->history(peerId);
|
||||
}
|
||||
return entry.musicIdToMsg.emplace(id, entry.history->makeMessage({
|
||||
.id = entry.history->nextNonHistoryEntryId(),
|
||||
.flags = (MessageFlag::FakeHistoryItem
|
||||
| MessageFlag::HasFromId
|
||||
| MessageFlag::SavedMusicItem),
|
||||
.from = entry.history->peer->id,
|
||||
.date = base::unixtime::now(),
|
||||
}, id, TextWithEntities())).first->second.get();
|
||||
}
|
||||
|
||||
void SavedMusic::loadIds() {
|
||||
if (_loadIdsRequest
|
||||
|| (_lastReceived
|
||||
&& (crl::now() - _lastReceived < kReloadIdsEvery))) {
|
||||
return;
|
||||
}
|
||||
_loadIdsRequest = _owner->session().api().request(
|
||||
MTPaccount_GetSavedMusicIds(MTP_long(Api::CountHash(_myIds)))
|
||||
).done([=](const MTPaccount_SavedMusicIds &result) {
|
||||
_loadIdsRequest = 0;
|
||||
_lastReceived = crl::now();
|
||||
result.match([&](const MTPDaccount_savedMusicIds &data) {
|
||||
_myIds = data.vids().v
|
||||
| ranges::views::transform(&MTPlong::v)
|
||||
| ranges::to_vector;
|
||||
}, [](const MTPDaccount_savedMusicIdsNotModified &) {
|
||||
});
|
||||
}).fail([=] {
|
||||
_loadIdsRequest = 0;
|
||||
_lastReceived = crl::now();
|
||||
}).send();
|
||||
}
|
||||
|
||||
bool SavedMusic::has(not_null<DocumentData*> document) const {
|
||||
return ranges::contains(_myIds, document->id);
|
||||
}
|
||||
|
||||
void SavedMusic::save(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin) {
|
||||
const auto peerId = _owner->session().userPeerId();
|
||||
auto &entry = _entries[peerId];
|
||||
if (entry.list.empty() && !entry.loaded) {
|
||||
loadMore(peerId);
|
||||
}
|
||||
if (has(document)) {
|
||||
return;
|
||||
}
|
||||
const auto item = musicIdToMsg(peerId, entry, document);
|
||||
entry.list.insert(begin(entry.list), item);
|
||||
if (entry.total >= 0) {
|
||||
++entry.total;
|
||||
}
|
||||
_myIds.insert(begin(_myIds), document->id);
|
||||
|
||||
const auto send = [=](auto resend) -> void {
|
||||
const auto usedFileReference = document->fileReference();
|
||||
_owner->session().api().request(MTPaccount_SaveMusic(
|
||||
MTP_flags(0),
|
||||
document->mtpInput(),
|
||||
MTPInputDocument()
|
||||
)).fail([=](const MTP::Error &error) {
|
||||
if (error.code() == 400
|
||||
&& error.type().startsWith(u"FILE_REFERENCE_"_q)) {
|
||||
document->session().api().refreshFileReference(origin, [=](
|
||||
const auto &) {
|
||||
if (document->fileReference() != usedFileReference) {
|
||||
resend(resend);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).send();
|
||||
};
|
||||
send(send);
|
||||
|
||||
_changed.fire_copy(peerId);
|
||||
}
|
||||
|
||||
void SavedMusic::remove(not_null<DocumentData*> document) {
|
||||
const auto peerId = _owner->session().userPeerId();
|
||||
auto &entry = _entries[peerId];
|
||||
const auto i = ranges::find(entry.list, document, ItemDocument);
|
||||
if (i != end(entry.list)) {
|
||||
entry.musicIdFromMsgId.remove((*i)->id);
|
||||
entry.list.erase(i);
|
||||
if (entry.total > 0) {
|
||||
entry.total = std::max(entry.total - 1, 0);
|
||||
}
|
||||
}
|
||||
entry.musicIdToMsg.remove(document);
|
||||
_myIds.erase(ranges::remove(_myIds, document->id), end(_myIds));
|
||||
_owner->session().api().request(MTPaccount_SaveMusic(
|
||||
MTP_flags(MTPaccount_SaveMusic::Flag::f_unsave),
|
||||
document->mtpInput(),
|
||||
MTPInputDocument()
|
||||
)).send();
|
||||
_changed.fire_copy(peerId);
|
||||
}
|
||||
|
||||
void SavedMusic::apply(not_null<UserData*> user, const MTPDocument *last) {
|
||||
const auto peerId = user->id;
|
||||
auto &entry = _entries[peerId];
|
||||
if (!last) {
|
||||
if (const auto requestId = base::take(entry.requestId)) {
|
||||
_owner->session().api().request(requestId).cancel();
|
||||
}
|
||||
entry = Entry{ .total = 0, .loaded = true };
|
||||
_changed.fire_copy(peerId);
|
||||
return;
|
||||
}
|
||||
const auto document = _owner->processDocument(*last);
|
||||
const auto i = ranges::find(entry.list, document, ItemDocument);
|
||||
if (i != end(entry.list)) {
|
||||
if (i == begin(entry.list)) {
|
||||
return;
|
||||
}
|
||||
ranges::rotate(begin(entry.list), i, i + 1);
|
||||
_changed.fire_copy(peerId);
|
||||
loadMore(peerId, true);
|
||||
return;
|
||||
}
|
||||
entry.list.insert(
|
||||
begin(entry.list),
|
||||
musicIdToMsg(peerId, entry, document));
|
||||
_changed.fire_copy(peerId);
|
||||
if (entry.loaded) {
|
||||
loadMore(peerId, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool SavedMusic::countKnown(PeerId peerId) const {
|
||||
if (!Supported(peerId)) {
|
||||
return true;
|
||||
}
|
||||
const auto entry = lookupEntry(peerId);
|
||||
return entry && entry->total >= 0;
|
||||
}
|
||||
|
||||
int SavedMusic::count(PeerId peerId) const {
|
||||
if (!Supported(peerId)) {
|
||||
return 0;
|
||||
}
|
||||
const auto entry = lookupEntry(peerId);
|
||||
return entry ? std::max(entry->total, 0) : 0;
|
||||
}
|
||||
|
||||
const std::vector<not_null<HistoryItem*>> &SavedMusic::list(
|
||||
PeerId peerId) const {
|
||||
static const auto empty = std::vector<not_null<HistoryItem*>>();
|
||||
if (!Supported(peerId)) {
|
||||
return empty;
|
||||
}
|
||||
|
||||
const auto entry = lookupEntry(peerId);
|
||||
return entry ? entry->list : empty;
|
||||
}
|
||||
|
||||
void SavedMusic::loadMore(PeerId peerId) {
|
||||
loadMore(peerId, false);
|
||||
}
|
||||
|
||||
void SavedMusic::loadMore(PeerId peerId, bool reload) {
|
||||
if (!Supported(peerId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &entry = _entries[peerId];
|
||||
if (!entry.reloading && reload) {
|
||||
_owner->session().api().request(
|
||||
base::take(entry.requestId)).cancel();
|
||||
}
|
||||
if ((!reload && entry.loaded) || entry.requestId) {
|
||||
return;
|
||||
}
|
||||
const auto user = _owner->peer(peerId)->asUser();
|
||||
Assert(user != nullptr);
|
||||
|
||||
entry.reloading = reload;
|
||||
entry.requestId = _owner->session().api().request(MTPusers_GetSavedMusic(
|
||||
user->inputUser,
|
||||
MTP_int(reload ? 0 : entry.list.size()),
|
||||
MTP_int(kPerPage),
|
||||
MTP_long(reload ? firstPageHash(entry) : 0)
|
||||
)).done([=](const MTPusers_SavedMusic &result) {
|
||||
auto &entry = _entries[peerId];
|
||||
entry.requestId = 0;
|
||||
const auto reloaded = base::take(entry.reloading);
|
||||
result.match([&](const MTPDusers_savedMusicNotModified &) {
|
||||
}, [&](const MTPDusers_savedMusic &data) {
|
||||
const auto list = data.vdocuments().v;
|
||||
const auto count = int(list.size());
|
||||
entry.total = std::max(count, data.vcount().v);
|
||||
if (reloaded) {
|
||||
entry.list.clear();
|
||||
}
|
||||
for (const auto &item : list) {
|
||||
const auto document = _owner->processDocument(item);
|
||||
if (!ranges::contains(entry.list, document, ItemDocument)) {
|
||||
entry.list.push_back(
|
||||
musicIdToMsg(peerId, entry, document));
|
||||
}
|
||||
}
|
||||
entry.loaded = list.empty() || (count == entry.list.size());
|
||||
});
|
||||
_changed.fire_copy(peerId);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
auto &entry = _entries[peerId];
|
||||
entry.requestId = 0;
|
||||
entry.total = int(entry.list.size());
|
||||
entry.loaded = true;
|
||||
_changed.fire_copy(peerId);
|
||||
}).send();
|
||||
}
|
||||
|
||||
uint64 SavedMusic::firstPageHash(const Entry &entry) const {
|
||||
return Api::CountHash(entry.list
|
||||
| ranges::views::transform(ItemDocument)
|
||||
| ranges::views::transform(&DocumentData::id)
|
||||
| ranges::views::take(kPerPage));
|
||||
}
|
||||
|
||||
rpl::producer<PeerId> SavedMusic::changed() const {
|
||||
return _changed.events();
|
||||
}
|
||||
|
||||
SavedMusic::Entry *SavedMusic::lookupEntry(PeerId peerId) {
|
||||
if (!Supported(peerId)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = _entries.find(peerId);
|
||||
if (it == end(_entries)) {
|
||||
return nullptr;
|
||||
}
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
const SavedMusic::Entry *SavedMusic::lookupEntry(PeerId peerId) const {
|
||||
return const_cast<SavedMusic*>(this)->lookupEntry(peerId);
|
||||
}
|
||||
|
||||
rpl::producer<SavedMusicSlice> SavedMusicList(
|
||||
not_null<PeerData*> peer,
|
||||
HistoryItem *aroundId,
|
||||
int limit) {
|
||||
if (!peer->isUser()) {
|
||||
return rpl::single(SavedMusicSlice({}, 0, 0, 0));
|
||||
}
|
||||
return [=](auto consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
|
||||
struct State {
|
||||
SavedMusicSlice slice;
|
||||
base::has_weak_ptr guard;
|
||||
bool scheduled = false;
|
||||
};
|
||||
const auto state = lifetime.make_state<State>();
|
||||
|
||||
const auto push = [=] {
|
||||
state->scheduled = false;
|
||||
|
||||
const auto peerId = peer->id;
|
||||
const auto savedMusic = &peer->owner().savedMusic();
|
||||
if (!savedMusic->countKnown(peerId)) {
|
||||
return;
|
||||
}
|
||||
const auto &loaded = savedMusic->list(peerId);
|
||||
const auto count = savedMusic->count(peerId);
|
||||
auto i = aroundId
|
||||
? ranges::find(loaded, not_null(aroundId))
|
||||
: begin(loaded);
|
||||
if (i == end(loaded)) {
|
||||
i = begin(loaded);
|
||||
}
|
||||
const auto hasBefore = int(i - begin(loaded));
|
||||
const auto hasAfter = int(end(loaded) - i);
|
||||
if (hasAfter < limit) {
|
||||
savedMusic->loadMore(peerId);
|
||||
}
|
||||
const auto takeBefore = std::min(hasBefore, limit);
|
||||
const auto takeAfter = std::min(hasAfter, limit);
|
||||
auto ids = std::vector<not_null<HistoryItem*>>();
|
||||
ids.reserve(takeBefore + takeAfter);
|
||||
for (auto j = i - takeBefore; j != i + takeAfter; ++j) {
|
||||
ids.push_back(*j);
|
||||
}
|
||||
const auto added = int(ids.size());
|
||||
state->slice = SavedMusicSlice(
|
||||
std::move(ids),
|
||||
count,
|
||||
(hasBefore - takeBefore),
|
||||
count - hasBefore - added);
|
||||
consumer.put_next_copy(state->slice);
|
||||
};
|
||||
const auto schedule = [=] {
|
||||
if (state->scheduled) {
|
||||
return;
|
||||
}
|
||||
state->scheduled = true;
|
||||
Ui::PostponeCall(&state->guard, [=] {
|
||||
if (state->scheduled) {
|
||||
push();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const auto peerId = peer->id;
|
||||
const auto savedMusic = &peer->owner().savedMusic();
|
||||
savedMusic->changed(
|
||||
) | rpl::filter(
|
||||
rpl::mappers::_1 == peerId
|
||||
) | rpl::start_with_next(schedule, lifetime);
|
||||
|
||||
if (!savedMusic->countKnown(peerId)) {
|
||||
savedMusic->loadMore(peerId);
|
||||
}
|
||||
|
||||
push();
|
||||
|
||||
return lifetime;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
87
Telegram/SourceFiles/data/data_saved_music.h
Normal file
87
Telegram/SourceFiles/data/data_saved_music.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "data/data_abstract_sparse_ids.h"
|
||||
|
||||
#include "history/history_item.h"
|
||||
|
||||
class PeerData;
|
||||
|
||||
namespace Data {
|
||||
|
||||
class Session;
|
||||
struct FileOrigin;
|
||||
|
||||
class SavedMusic final {
|
||||
public:
|
||||
explicit SavedMusic(not_null<Session*> owner);
|
||||
~SavedMusic();
|
||||
|
||||
[[nodiscard]] static bool Supported(PeerId peerId);
|
||||
|
||||
[[nodiscard]] bool countKnown(PeerId peerId) const;
|
||||
[[nodiscard]] int count(PeerId peerId) const;
|
||||
[[nodiscard]] const std::vector<not_null<HistoryItem*>> &list(
|
||||
PeerId peerId) const;
|
||||
void loadMore(PeerId peerId);
|
||||
|
||||
[[nodiscard]] rpl::producer<PeerId> changed() const;
|
||||
|
||||
void loadIds();
|
||||
[[nodiscard]] bool has(not_null<DocumentData*> document) const;
|
||||
void save(not_null<DocumentData*> document, FileOrigin origin);
|
||||
void remove(not_null<DocumentData*> document);
|
||||
|
||||
void apply(not_null<UserData*> user, const MTPDocument *last);
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>;
|
||||
|
||||
struct Entry {
|
||||
base::flat_map<MsgId, not_null<DocumentData*>> musicIdFromMsgId;
|
||||
base::flat_map<not_null<DocumentData*>, OwnedItem> musicIdToMsg;
|
||||
std::vector<not_null<HistoryItem*>> list;
|
||||
History *history = nullptr;
|
||||
mtpRequestId requestId = 0;
|
||||
int total = -1;
|
||||
bool loaded = false;
|
||||
bool reloading = false;
|
||||
};
|
||||
|
||||
void loadMore(PeerId peerId, bool reload);
|
||||
[[nodiscard]] Entry *lookupEntry(PeerId peerId);
|
||||
[[nodiscard]] const Entry *lookupEntry(PeerId peerId) const;
|
||||
[[nodiscard]] uint64 firstPageHash(const Entry &entry) const;
|
||||
[[nodiscard]] not_null<HistoryItem*> musicIdToMsg(
|
||||
PeerId peerId,
|
||||
Entry &entry,
|
||||
not_null<DocumentData*> id);
|
||||
|
||||
const not_null<Session*> _owner;
|
||||
|
||||
std::vector<DocumentId> _myIds;
|
||||
crl::time _lastReceived = 0;
|
||||
mtpRequestId _loadIdsRequest = 0;
|
||||
|
||||
std::unordered_map<PeerId, Entry> _entries;
|
||||
rpl::event_stream<PeerId> _changed;
|
||||
|
||||
};
|
||||
|
||||
using SavedMusicSlice = AbstractSparseIds<
|
||||
std::vector<not_null<HistoryItem*>>>;
|
||||
|
||||
[[nodiscard]] rpl::producer<SavedMusicSlice> SavedMusicList(
|
||||
not_null<PeerData*> peer,
|
||||
HistoryItem *aroundId,
|
||||
int limit);
|
||||
|
||||
} // namespace Data
|
||||
@@ -66,6 +66,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_forum_icons.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_saved_music.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "data/data_streaming.h"
|
||||
@@ -85,6 +86,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Data {
|
||||
namespace {
|
||||
|
||||
constexpr auto kNextForUpgradeGiftTimeout = 5 * crl::time(1000);
|
||||
|
||||
using ViewElement = HistoryView::Element;
|
||||
|
||||
// s: box 100x100
|
||||
@@ -248,6 +251,7 @@ Session::Session(not_null<Main::Session*> session)
|
||||
, _notifySettings(std::make_unique<NotifySettings>(this))
|
||||
, _customEmojiManager(std::make_unique<CustomEmojiManager>(this))
|
||||
, _stories(std::make_unique<Stories>(this))
|
||||
, _savedMusic(std::make_unique<SavedMusic>(this))
|
||||
, _savedMessages(std::make_unique<SavedMessages>(this))
|
||||
, _chatbots(std::make_unique<Chatbots>(this))
|
||||
, _businessInfo(std::make_unique<BusinessInfo>(this))
|
||||
@@ -405,6 +409,7 @@ void Session::clear() {
|
||||
channel->setFlags(channel->flags()
|
||||
& ~(ChannelDataFlag::Forum | ChannelDataFlag::MonoforumAdmin));
|
||||
}
|
||||
_savedMusic->clear();
|
||||
_savedMessages->clear();
|
||||
|
||||
_sendActionManager->clear();
|
||||
@@ -2155,6 +2160,57 @@ rpl::producer<> Session::pinnedDialogsOrderUpdated() const {
|
||||
return _pinnedDialogsOrderUpdated.events();
|
||||
}
|
||||
|
||||
void Session::nextForUpgradeGiftInvalidate(not_null<PeerData*> owner) {
|
||||
_nextForUpgradeGifts.remove(owner);
|
||||
}
|
||||
|
||||
void Session::nextForUpgradeGiftRequest(
|
||||
not_null<PeerData*> owner,
|
||||
Fn<void(std::optional<Data::SavedStarGift>)> done) {
|
||||
auto &entry = _nextForUpgradeGifts[owner];
|
||||
if (entry.requestId) {
|
||||
entry.done = std::move(done);
|
||||
return;
|
||||
} else if (crl::now() - entry.received < kNextForUpgradeGiftTimeout) {
|
||||
done(entry.gift);
|
||||
return;
|
||||
}
|
||||
entry.done = std::move(done);
|
||||
|
||||
const auto finishWith = [=](std::optional<Data::SavedStarGift> gift) {
|
||||
auto &entry = _nextForUpgradeGifts[owner];
|
||||
entry.requestId = 0;
|
||||
entry.gift = std::move(gift);
|
||||
entry.received = crl::now();
|
||||
base::take(entry.done)(entry.gift);
|
||||
};
|
||||
using Flag = MTPpayments_GetSavedStarGifts::Flag;
|
||||
entry.requestId = _session->api().request(
|
||||
MTPpayments_GetSavedStarGifts(
|
||||
MTP_flags(Flag::f_exclude_unique
|
||||
| Flag::f_exclude_unlimited
|
||||
| Flag::f_exclude_unupgradable),
|
||||
owner->input,
|
||||
MTPint(), // collection_id
|
||||
MTP_string(), // offset
|
||||
MTP_int(1)) // limit
|
||||
).done([=](const MTPpayments_SavedStarGifts &result) {
|
||||
const auto &data = result.data();
|
||||
processUsers(data.vusers());
|
||||
processChats(data.vchats());
|
||||
const auto &list = data.vgifts().v;
|
||||
if (list.empty()) {
|
||||
finishWith(std::nullopt);
|
||||
} else if (auto parsed = Api::FromTL(owner, list[0])) {
|
||||
finishWith(std::move(*parsed));
|
||||
} else {
|
||||
finishWith(std::nullopt);
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
finishWith(std::nullopt);
|
||||
}).send();
|
||||
}
|
||||
|
||||
Session::CreditsSubsRebuilderPtr Session::createCreditsSubsRebuilder() {
|
||||
if (auto result = activeCreditsSubsRebuilder()) {
|
||||
return result;
|
||||
@@ -4335,6 +4391,9 @@ void Session::unregisterPhotoItem(
|
||||
void Session::registerDocumentItem(
|
||||
not_null<const DocumentData*> document,
|
||||
not_null<HistoryItem*> item) {
|
||||
if (document->isMusicForProfile()) {
|
||||
document->owner().savedMusic().loadIds();
|
||||
}
|
||||
_documentItems[document].insert(item);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ class GroupCall;
|
||||
class NotifySettings;
|
||||
class CustomEmojiManager;
|
||||
class Stories;
|
||||
class SavedMusic;
|
||||
class SavedMessages;
|
||||
class Chatbots;
|
||||
class BusinessInfo;
|
||||
@@ -184,6 +185,9 @@ public:
|
||||
[[nodiscard]] Stories &stories() const {
|
||||
return *_stories;
|
||||
}
|
||||
[[nodiscard]] SavedMusic &savedMusic() const {
|
||||
return *_savedMusic;
|
||||
}
|
||||
[[nodiscard]] SavedMessages &savedMessages() const {
|
||||
return *_savedMessages;
|
||||
}
|
||||
@@ -376,6 +380,11 @@ public:
|
||||
void notifyPinnedDialogsOrderUpdated();
|
||||
[[nodiscard]] rpl::producer<> pinnedDialogsOrderUpdated() const;
|
||||
|
||||
void nextForUpgradeGiftInvalidate(not_null<PeerData*> owner);
|
||||
void nextForUpgradeGiftRequest(
|
||||
not_null<PeerData*> owner,
|
||||
Fn<void(std::optional<Data::SavedStarGift>)> done);
|
||||
|
||||
using CreditsSubsRebuilder = rpl::event_stream<CreditsStatusSlice>;
|
||||
using CreditsSubsRebuilderPtr = std::shared_ptr<CreditsSubsRebuilder>;
|
||||
[[nodiscard]] CreditsSubsRebuilderPtr createCreditsSubsRebuilder();
|
||||
@@ -881,6 +890,13 @@ public:
|
||||
private:
|
||||
using Messages = std::unordered_map<MsgId, not_null<HistoryItem*>>;
|
||||
|
||||
struct NextToUpgradeGift {
|
||||
std::optional<Data::SavedStarGift> gift;
|
||||
Fn<void(std::optional<Data::SavedStarGift>)> done;
|
||||
crl::time received = 0;
|
||||
mtpRequestId requestId = 0;
|
||||
};
|
||||
|
||||
void suggestStartExport();
|
||||
|
||||
void setupMigrationViewer();
|
||||
@@ -1223,6 +1239,7 @@ private:
|
||||
const std::unique_ptr<NotifySettings> _notifySettings;
|
||||
const std::unique_ptr<CustomEmojiManager> _customEmojiManager;
|
||||
const std::unique_ptr<Stories> _stories;
|
||||
const std::unique_ptr<SavedMusic> _savedMusic;
|
||||
const std::unique_ptr<SavedMessages> _savedMessages;
|
||||
const std::unique_ptr<Chatbots> _chatbots;
|
||||
const std::unique_ptr<BusinessInfo> _businessInfo;
|
||||
@@ -1232,6 +1249,10 @@ private:
|
||||
|
||||
std::unique_ptr<StarsRatingPending> _pendingStarsRating;
|
||||
|
||||
base::flat_map<
|
||||
not_null<PeerData*>,
|
||||
NextToUpgradeGift> _nextForUpgradeGifts;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
@@ -7,18 +7,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_shared_media.h"
|
||||
|
||||
#include <rpl/combine.h>
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "storage/storage_facade.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "core/crash_reports.h"
|
||||
#include "data/components/scheduled_messages.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_saved_music.h"
|
||||
#include "data/data_session.h"
|
||||
#include "core/crash_reports.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "main/main_session.h"
|
||||
#include "storage/storage_facade.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -233,6 +233,39 @@ rpl::producer<SparseIdsMergedSlice> SharedScheduledMediaViewer(
|
||||
});
|
||||
}
|
||||
|
||||
rpl::producer<SparseIdsMergedSlice> SavedMusicMediaViewer(
|
||||
not_null<Main::Session*> session,
|
||||
SharedMediaMergedKey key,
|
||||
int limitBefore,
|
||||
int limitAfter) {
|
||||
Expects((key.mergedKey.universalId != 0)
|
||||
|| (limitBefore == 0 && limitAfter == 0));
|
||||
|
||||
const auto peerId = key.mergedKey.peerId;
|
||||
const auto item = key.mergedKey.universalId
|
||||
? session->data().message(peerId, key.mergedKey.universalId)
|
||||
: nullptr;
|
||||
|
||||
return Data::SavedMusicList(
|
||||
session->data().peer(peerId),
|
||||
item,
|
||||
std::max(limitBefore, limitAfter)
|
||||
) | rpl::map([=](const Data::SavedMusicSlice &slice) {
|
||||
auto list = std::vector<MsgId>();
|
||||
list.reserve(slice.size());
|
||||
for (auto i = 0, count = int(slice.size()); i != count; ++i) {
|
||||
list.push_back(slice[i]->id);
|
||||
}
|
||||
return SparseIdsMergedSlice(
|
||||
key.mergedKey,
|
||||
SparseUnsortedIdsSlice(
|
||||
std::move(list),
|
||||
slice.fullCount(),
|
||||
slice.skippedBefore(),
|
||||
slice.skippedAfter()));
|
||||
});
|
||||
}
|
||||
|
||||
rpl::producer<SparseIdsMergedSlice> SharedMediaMergedViewer(
|
||||
not_null<Main::Session*> session,
|
||||
SharedMediaMergedKey key,
|
||||
@@ -490,6 +523,19 @@ rpl::producer<SharedMediaWithLastSlice> SharedMediaWithLastViewer(
|
||||
std::move(update),
|
||||
std::nullopt));
|
||||
});
|
||||
} else if (key.topicRootId == SharedMediaWithLastSlice::kSavedMusicTopicId) {
|
||||
return SavedMusicMediaViewer(
|
||||
session,
|
||||
std::move(viewerKey),
|
||||
limitBefore,
|
||||
limitAfter
|
||||
) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) {
|
||||
consumer.put_next(SharedMediaWithLastSlice(
|
||||
session,
|
||||
key,
|
||||
std::move(update),
|
||||
std::nullopt));
|
||||
});
|
||||
}
|
||||
return rpl::combine(
|
||||
SharedMediaMergedViewer(
|
||||
|
||||
@@ -52,6 +52,12 @@ rpl::producer<SparseIdsMergedSlice> SharedScheduledMediaViewer(
|
||||
int limitBefore,
|
||||
int limitAfter);
|
||||
|
||||
rpl::producer<SparseIdsMergedSlice> SavedMusicMediaViewer(
|
||||
not_null<Main::Session*> session,
|
||||
SharedMediaMergedKey key,
|
||||
int limitBefore,
|
||||
int limitAfter);
|
||||
|
||||
rpl::producer<SparseIdsMergedSlice> SharedMediaMergedViewer(
|
||||
not_null<Main::Session*> session,
|
||||
SharedMediaMergedKey key,
|
||||
@@ -70,6 +76,8 @@ public:
|
||||
|
||||
static constexpr auto kScheduledTopicId
|
||||
= SparseIdsMergedSlice::kScheduledTopicId;
|
||||
static constexpr auto kSavedMusicTopicId
|
||||
= SparseIdsMergedSlice::kSavedMusicTopicId;
|
||||
struct Key {
|
||||
Key(
|
||||
PeerId peerId,
|
||||
|
||||
@@ -28,6 +28,7 @@ class SparseIdsMergedSlice {
|
||||
public:
|
||||
using UniversalMsgId = MsgId;
|
||||
static constexpr MsgId kScheduledTopicId = ScheduledMaxMsgId;
|
||||
static constexpr MsgId kSavedMusicTopicId = ScheduledMaxMsgId + 1;
|
||||
|
||||
struct Key {
|
||||
Key(
|
||||
|
||||
@@ -37,19 +37,38 @@ struct UniqueGiftOriginalDetails {
|
||||
TextWithEntities message;
|
||||
};
|
||||
|
||||
struct UniqueGiftValue {
|
||||
QString currency;
|
||||
int64 valuePrice = 0;
|
||||
CreditsAmount initialPriceStars;
|
||||
int64 initialSalePrice = 0;
|
||||
TimeId initialSaleDate = 0;
|
||||
int64 lastSalePrice = 0;
|
||||
TimeId lastSaleDate = 0;
|
||||
int64 averagePrice = 0;
|
||||
int64 minimumPrice = 0;
|
||||
int forSaleOnTelegram = 0;
|
||||
int forSaleOnFragment = 0;
|
||||
QString fragmentUrl;
|
||||
bool lastSaleFragment = false;
|
||||
};
|
||||
|
||||
struct UniqueGift {
|
||||
CollectibleId id = 0;
|
||||
uint64 initialGiftId = 0;
|
||||
QString slug;
|
||||
QString title;
|
||||
QString ownerAddress;
|
||||
QString ownerName;
|
||||
PeerId ownerId = 0;
|
||||
PeerData *releasedBy = nullptr;
|
||||
PeerData *themeUser = nullptr;
|
||||
int64 nanoTonForResale = -1;
|
||||
int starsForResale = -1;
|
||||
int starsForTransfer = -1;
|
||||
int number = 0;
|
||||
bool onlyAcceptTon = false;
|
||||
bool canBeTheme = false;
|
||||
TimeId exportAt = 0;
|
||||
TimeId canTransferAt = 0;
|
||||
TimeId canResellAt = 0;
|
||||
@@ -57,6 +76,7 @@ struct UniqueGift {
|
||||
UniqueGiftPattern pattern;
|
||||
UniqueGiftBackdrop backdrop;
|
||||
UniqueGiftOriginalDetails originalDetails;
|
||||
std::shared_ptr<UniqueGiftValue> value;
|
||||
};
|
||||
|
||||
[[nodiscard]] QString UniqueGiftName(const UniqueGift &gift);
|
||||
@@ -86,6 +106,7 @@ struct StarGift {
|
||||
int perUserRemains = 0;
|
||||
TimeId firstSaleDate = 0;
|
||||
TimeId lastSaleDate = 0;
|
||||
TimeId lockedUntilDate = 0;
|
||||
bool resellTonOnly : 1 = false;
|
||||
bool requirePremium : 1 = false;
|
||||
bool upgradable : 1 = false;
|
||||
@@ -154,6 +175,7 @@ struct SavedStarGift {
|
||||
TextWithEntities message;
|
||||
int64 starsConverted = 0;
|
||||
int64 starsUpgradedBySender = 0;
|
||||
QString giftPrepayUpgradeHash;
|
||||
PeerId fromId = 0;
|
||||
TimeId date = 0;
|
||||
bool upgradable = false;
|
||||
|
||||
@@ -1721,7 +1721,7 @@ rpl::producer<StoryAlbumIdsKey> Stories::albumIdsChanged() const {
|
||||
|
||||
int Stories::albumIdsCount(PeerId peerId, int albumId) const {
|
||||
const auto set = albumIdsSet(peerId, albumId);
|
||||
return set ? set->total : 0;
|
||||
return set ? std::max(set->total, 0) : 0;
|
||||
}
|
||||
|
||||
bool Stories::albumIdsCountKnown(PeerId peerId, int albumId) const {
|
||||
|
||||
@@ -358,6 +358,7 @@ enum class MessageFlag : uint64 {
|
||||
TonPaidSuggested = (1ULL << 53),
|
||||
|
||||
StoryInProfile = (1ULL << 54),
|
||||
SavedMusicItem = (1ULL << 55),
|
||||
};
|
||||
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
||||
using MessageFlags = base::flags<MessageFlag>;
|
||||
|
||||
@@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/business/data_business_common.h"
|
||||
#include "data/business/data_business_info.h"
|
||||
#include "data/components/credits.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "data/data_saved_music.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_peer_bot_command.h"
|
||||
@@ -788,7 +790,16 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
|
||||
user->setCommonChatsCount(update.vcommon_chats_count().v);
|
||||
user->setPeerGiftsCount(update.vstargifts_count().value_or_empty());
|
||||
user->checkFolder(update.vfolder_id().value_or_empty());
|
||||
user->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
|
||||
if (const auto theme = update.vtheme()) {
|
||||
theme->match([&](const MTPDchatTheme &data) {
|
||||
user->setThemeToken(qs(data.vemoticon()));
|
||||
}, [&](const MTPDchatThemeUniqueGift &data) {
|
||||
user->setThemeToken(
|
||||
user->owner().cloudThemes().processGiftThemeGetToken(data));
|
||||
});
|
||||
} else {
|
||||
user->setThemeToken(QString());
|
||||
}
|
||||
user->setTranslationDisabled(update.is_translations_disabled());
|
||||
user->setPrivateForwardName(
|
||||
update.vprivate_forward_name().value_or_empty());
|
||||
@@ -907,6 +918,7 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
|
||||
}
|
||||
|
||||
user->owner().stories().apply(user, update.vstories());
|
||||
user->owner().savedMusic().apply(user, update.vsaved_music());
|
||||
|
||||
user->fullUpdated();
|
||||
}
|
||||
|
||||
@@ -21,11 +21,6 @@ class Thread;
|
||||
class Forum;
|
||||
class ForumTopic;
|
||||
|
||||
enum class DefaultNotify {
|
||||
User,
|
||||
Group,
|
||||
Broadcast,
|
||||
};
|
||||
[[nodiscard]] DefaultNotify DefaultNotifyType(
|
||||
not_null<const PeerData*> peer);
|
||||
|
||||
|
||||
@@ -11,6 +11,12 @@ namespace Data {
|
||||
|
||||
class NotifyPeerSettingsValue;
|
||||
|
||||
enum class DefaultNotify : uint8_t {
|
||||
User,
|
||||
Group,
|
||||
Broadcast,
|
||||
};
|
||||
|
||||
struct NotifySound {
|
||||
QString title;
|
||||
QString data;
|
||||
|
||||
117
Telegram/SourceFiles/data/notify/data_peer_notify_volume.cpp
Normal file
117
Telegram/SourceFiles/data/notify/data_peer_notify_volume.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
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/notify/data_peer_notify_volume.h"
|
||||
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "data/notify/data_peer_notify_settings.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "core/application.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
Data::VolumeController DefaultRingtonesVolumeController(
|
||||
not_null<Main::Session*> session,
|
||||
Data::DefaultNotify defaultNotify) {
|
||||
return Data::VolumeController{
|
||||
.volume = [=]() -> ushort {
|
||||
const auto volume = session->settings().ringtoneVolume(
|
||||
defaultNotify);
|
||||
return volume ? volume : 100;
|
||||
},
|
||||
.saveVolume = [=](ushort volume) {
|
||||
session->settings().setRingtoneVolume(defaultNotify, volume);
|
||||
session->saveSettingsDelayed();
|
||||
}};
|
||||
}
|
||||
|
||||
Data::VolumeController ThreadRingtonesVolumeController(
|
||||
not_null<Data::Thread*> thread) {
|
||||
return Data::VolumeController{
|
||||
.volume = [=]() -> ushort {
|
||||
const auto volume = thread->session().settings().ringtoneVolume(
|
||||
thread->peer()->id,
|
||||
thread->topicRootId(),
|
||||
thread->monoforumPeerId());
|
||||
return volume ? volume : 100;
|
||||
},
|
||||
.saveVolume = [=](ushort volume) {
|
||||
thread->session().settings().setRingtoneVolume(
|
||||
thread->peer()->id,
|
||||
thread->topicRootId(),
|
||||
thread->monoforumPeerId(),
|
||||
volume);
|
||||
thread->session().saveSettingsDelayed();
|
||||
}};
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
|
||||
void AddRingtonesVolumeSlider(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<bool> toggleOn,
|
||||
rpl::producer<QString> subtitle,
|
||||
Data::VolumeController volumeController) {
|
||||
Expects(volumeController.volume && volumeController.saveVolume);
|
||||
|
||||
const auto volumeWrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
volumeWrap->toggleOn(
|
||||
rpl::combine(
|
||||
Core::App().notifications().volumeSupportedValue(),
|
||||
std::move(toggleOn)
|
||||
) | rpl::map(
|
||||
rpl::mappers::_1 && rpl::mappers::_2
|
||||
) | rpl::distinct_until_changed(),
|
||||
anim::type::normal);
|
||||
volumeWrap->finishAnimating();
|
||||
|
||||
Ui::AddSubsectionTitle(volumeWrap->entity(), std::move(subtitle));
|
||||
auto sliderWithLabel = Settings::MakeSliderWithLabel(
|
||||
volumeWrap->entity(),
|
||||
st::settingsScale,
|
||||
st::settingsScaleLabel,
|
||||
st::normalFont->spacew * 2,
|
||||
st::settingsScaleLabel.style.font->width("100%"),
|
||||
true);
|
||||
const auto slider = sliderWithLabel.slider;
|
||||
const auto label = sliderWithLabel.label;
|
||||
|
||||
volumeWrap->entity()->add(
|
||||
std::move(sliderWithLabel.widget),
|
||||
st::settingsBigScalePadding);
|
||||
|
||||
const auto updateLabel = [=](int volume) {
|
||||
label->setText(QString::number(volume) + '%');
|
||||
};
|
||||
|
||||
slider->setPseudoDiscrete(
|
||||
100,
|
||||
[=](int index) { return index + 1; },
|
||||
int(volumeController.volume()),
|
||||
updateLabel,
|
||||
[saveVolume = volumeController.saveVolume](int volume) {
|
||||
saveVolume(volume);
|
||||
});
|
||||
updateLabel(volumeController.volume());
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
45
Telegram/SourceFiles/data/notify/data_peer_notify_volume.h
Normal file
45
Telegram/SourceFiles/data/notify/data_peer_notify_volume.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
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 Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class VerticalLayout;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Data {
|
||||
|
||||
enum class DefaultNotify : uint8_t;
|
||||
class Thread;
|
||||
|
||||
struct VolumeController {
|
||||
Fn<ushort()> volume = nullptr;
|
||||
Fn<void(ushort)> saveVolume = nullptr;
|
||||
};
|
||||
|
||||
[[nodiscard]] VolumeController DefaultRingtonesVolumeController(
|
||||
not_null<Main::Session*> session,
|
||||
Data::DefaultNotify defaultNotify);
|
||||
|
||||
[[nodiscard]] VolumeController ThreadRingtonesVolumeController(
|
||||
not_null<Data::Thread*> thread);
|
||||
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
|
||||
void AddRingtonesVolumeSlider(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<bool> toggleOn,
|
||||
rpl::producer<QString> subtitle,
|
||||
Data::VolumeController volumeController);
|
||||
|
||||
} // namespace Ui
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user