diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index e44fb61bd..4db166fed 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -252,6 +252,8 @@ PRIVATE boxes/phone_banned_box.h boxes/pin_messages_box.cpp boxes/pin_messages_box.h + boxes/ringtones_box.cpp + boxes/ringtones_box.h boxes/self_destruction_box.cpp boxes/self_destruction_box.h boxes/send_files_box.cpp diff --git a/Telegram/Resources/icons/menu/sound_select.png b/Telegram/Resources/icons/menu/sound_select.png new file mode 100644 index 000000000..99587321d Binary files /dev/null and b/Telegram/Resources/icons/menu/sound_select.png differ diff --git a/Telegram/Resources/icons/menu/sound_select@2x.png b/Telegram/Resources/icons/menu/sound_select@2x.png new file mode 100644 index 000000000..387ea1788 Binary files /dev/null and b/Telegram/Resources/icons/menu/sound_select@2x.png differ diff --git a/Telegram/Resources/icons/menu/sound_select@3x.png b/Telegram/Resources/icons/menu/sound_select@3x.png new file mode 100644 index 000000000..93eb1f8bd Binary files /dev/null and b/Telegram/Resources/icons/menu/sound_select@3x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 7525ac789..9d0ebaf9f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -897,6 +897,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_mute_menu_duration_unmute" = "Unmute"; "lng_mute_menu_sound_on" = "Enable sound"; "lng_mute_menu_sound_off" = "Disable sound"; +"lng_mute_menu_sound_select" = "Select sound"; "lng_mute_box_title" = "Mute notifications for..."; "lng_mute_duration_hours#one" = "For {count} hour"; @@ -3113,6 +3114,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_telegram_features_url" = "https://t.me/TelegramTips"; +"lng_ringtones_box_title" = "Notification Sound"; +"lng_ringtones_box_cloud_subtitle" = "Telegram tones"; +"lng_ringtones_box_local_subtitle" = "System notes"; +"lng_ringtones_box_upload" = "Upload Sound"; +"lng_ringtones_box_upload_choose" = "Choose ringtone"; +"lng_ringtones_box_upload_button" = "Upload Sound"; +"lng_ringtones_box_default" = "Default"; +"lng_ringtones_box_no_sound" = "No sound"; +"lng_ringtones_box_error" = "Sorry, but your file is too big."; + // Wnd specific "lng_wnd_choose_program_menu" = "Choose Default Program..."; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 9d368b64a..6ef61c2d8 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -1055,3 +1055,12 @@ requestsRejectButton: RoundButton(defaultLightButton) { } requestAcceptPosition: point(71px, 58px); requestButtonsSkip: 9px; + +ringtonesBoxListHeight: 192; +ringtonesBoxButton: SettingsButton(defaultSettingsButton) { + textFg: lightButtonFg; + textFgOver: lightButtonFgOver; + padding: margins(56px, 10px, 22px, 8px); + iconLeft: 25px; +} +ringtonesBoxSkip: 7px; diff --git a/Telegram/SourceFiles/boxes/ringtones_box.cpp b/Telegram/SourceFiles/boxes/ringtones_box.cpp new file mode 100644 index 000000000..92a4d0a40 --- /dev/null +++ b/Telegram/SourceFiles/boxes/ringtones_box.cpp @@ -0,0 +1,233 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "boxes/ringtones_box.h" + +#include "api/api_ringtones.h" +#include "apiwrap.h" +#include "base/base_file_utilities.h" +#include "base/call_delayed.h" +#include "base/event_filter.h" +#include "core/file_utilities.h" +#include "core/mime_type.h" +#include "data/data_document.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 "settings/settings_common.h" +#include "ui/boxes/confirm_box.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/popup_menu.h" +#include "ui/widgets/scroll_area.h" +#include "ui/wrap/padding_wrap.h" +#include "ui/wrap/vertical_layout.h" +#include "styles/style_menu_icons.h" +#include "styles/style_boxes.h" +#include "styles/style_layers.h" +#include "styles/style_window.h" + +namespace { + +constexpr auto kDefaultValue = -1; +constexpr auto kNoSoundValue = -2; + +} // namespace + +void RingtonesBox( + not_null box, + not_null peer) { + box->setTitle(tr::lng_ringtones_box_title()); + + const auto container = box->verticalLayout(); + + auto padding = st::boxPadding; + padding.setTop(padding.bottom()); + + struct State { + std::shared_ptr group; + std::vector documentIds; + base::unique_qptr menu; + }; + const auto state = container->lifetime().make_state(State{ + std::make_shared( + peer->owner().notifySettings().sound(peer).none + ? kNoSoundValue + : kDefaultValue), + }); + + const auto addToGroup = [=]( + not_null verticalLayout, + int value, + const QString &text) { + const auto button = verticalLayout->add( + object_ptr( + verticalLayout, + state->group, + value, + text, + st::defaultCheckbox), + padding); + if (value < 0) { + return; + } + base::install_event_filter(button, [=](not_null e) { + if (state->menu || e->type() != QEvent::ContextMenu) { + return base::EventFilterResult::Continue; + } + state->menu = base::make_unique_q( + button, + st::popupMenuWithIcons); + auto callback = [=] { + const auto id = state->documentIds[value]; + peer->session().api().ringtones().remove(id); + }; + state->menu->addAction( + tr::lng_box_delete(tr::now), + std::move(callback), + &st::menuIconDelete); + state->menu->popup(QCursor::pos()); + return base::EventFilterResult::Cancel; + }); + }; + + peer->session().api().ringtones().uploadFails( + ) | rpl::start_with_next([=](const QString &error) { + if ((error == u"RINGTONE_DURATION_TOO_LONG"_q) + || (error == u"RINGTONE_SIZE_TOO_BIG"_q)) { + box->getDelegate()->show( + Ui::MakeInformBox(tr::lng_ringtones_box_error())); + } else if (error == u"RINGTONE_MIME_INVALID"_q) { + box->getDelegate()->show( + Ui::MakeInformBox(tr::lng_edit_media_invalid_file())); + } + }, box->lifetime()); + + Settings::AddSubsectionTitle( + container, + tr::lng_ringtones_box_cloud_subtitle()); + + const auto emptyContent = box->addRow( + object_ptr(container), + style::margins()); + const auto scroll = Ui::CreateChild( + emptyContent, + st::boxScroll); + emptyContent->widthValue( + ) | rpl::start_with_next([=](int width) { + scroll->resize(width, scroll->height()); + }, emptyContent->lifetime()); + scroll->heightValue( + ) | rpl::start_with_next([=](int height) { + emptyContent->resize(emptyContent->width(), height); + }, scroll->lifetime()); + + { + peer->session().api().ringtones().listUpdates( + ) | rpl::start_with_next([=] { + state->documentIds.clear(); + const auto list = scroll->setOwnedWidget( + object_ptr(scroll)); + list->sizeValue( + ) | rpl::start_with_next([=](const QSize &s) { + scroll->resize( + scroll->width(), + std::min(s.height(), st::ringtonesBoxListHeight)); + }, list->lifetime()); + list->resize(scroll->size()); + auto value = 0; + const auto checkedId = peer->owner().notifySettings().sound(peer); + for (const auto &id : peer->session().api().ringtones().list()) { + const auto document = peer->session().data().document(id); + addToGroup(list, value++, document->filename()); + state->documentIds.push_back(id); + if (checkedId.id && checkedId.id == id) { + state->group->setValue(value - 1); + } + } + }, scroll->lifetime()); + + peer->session().api().ringtones().requestList(); + } + + const auto upload = box->addRow( + Settings::CreateButton( + container, + tr::lng_ringtones_box_upload_button(), + st::ringtonesBoxButton, + { + &st::mainMenuAddAccount, + 0, + Settings::IconType::Round, + &st::windowBgActive + }), + style::margins()); + upload->addClickHandler([=] { + const auto delay = st::ringtonesBoxButton.ripple.hideDuration; + base::call_delayed(delay, crl::guard(box, [=] { + const auto callback = [=](const FileDialog::OpenResult &result) { + auto mime = QString(); + auto name = QString(); + auto content = result.remoteContent; + if (!result.paths.isEmpty()) { + auto info = QFileInfo(result.paths.front()); + mime = Core::MimeTypeForFile(info).name(); + name = info.fileName(); + auto f = QFile(result.paths.front()); + if (f.open(QIODevice::ReadOnly)) { + content = f.readAll(); + f.close(); + } + } else { + mime = Core::MimeTypeForData(content).name(); + name = "audio"; + } + + peer->session().api().ringtones().upload(name, mime, content); + }; + FileDialog::GetOpenPath( + box.get(), + tr::lng_ringtones_box_upload_choose(tr::now), + "Audio files (*.mp3)", + crl::guard(box, callback)); + })); + }); + + box->addSkip(st::ringtonesBoxSkip); + Settings::AddDivider(container); + + box->addSkip(st::ringtonesBoxSkip); + + Settings::AddSubsectionTitle( + container, + tr::lng_ringtones_box_local_subtitle()); + + addToGroup(container, kDefaultValue, tr::lng_ringtones_box_default({})); + addToGroup(container, kNoSoundValue, tr::lng_ringtones_box_no_sound({})); + + box->addSkip(st::ringtonesBoxSkip); + + box->setWidth(st::boxWideWidth); + box->addButton(tr::lng_settings_save(), [=] { + const auto value = state->group->value(); + auto sound = (value == kDefaultValue) + ? Data::NotifySound() + : (value == kNoSoundValue) + ? Data::NotifySound{ .none = true } + : Data::NotifySound{ .id = state->documentIds[value] }; + peer->owner().notifySettings().updateNotifySettings( + peer, + std::nullopt, + std::nullopt, + sound); + box->closeBox(); + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} diff --git a/Telegram/SourceFiles/boxes/ringtones_box.h b/Telegram/SourceFiles/boxes/ringtones_box.h new file mode 100644 index 000000000..bea10f50b --- /dev/null +++ b/Telegram/SourceFiles/boxes/ringtones_box.h @@ -0,0 +1,16 @@ +/* +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 "ui/layers/generic_box.h" + +class PeerData; + +void RingtonesBox( + not_null box, + not_null peer); diff --git a/Telegram/SourceFiles/menu/menu_mute.cpp b/Telegram/SourceFiles/menu/menu_mute.cpp index 231b353b2..4c9081a1f 100644 --- a/Telegram/SourceFiles/menu/menu_mute.cpp +++ b/Telegram/SourceFiles/menu/menu_mute.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "menu/menu_mute.h" +#include "boxes/ringtones_box.h" #include "data/data_peer.h" #include "data/data_session.h" #include "data/notify/data_notify_settings.h" @@ -230,6 +231,11 @@ void FillMuteMenu( Args args) { const auto peer = args.peer; + menu->addAction( + tr::lng_mute_menu_sound_select(tr::now), + [=, show = args.show] { show->showBox(Box(RingtonesBox, peer)); }, + &st::menuIconSoundSelect); + const auto soundIsNone = peer->owner().notifySettings().sound(peer).none; menu->addAction( soundIsNone diff --git a/Telegram/SourceFiles/ui/menu_icons.style b/Telegram/SourceFiles/ui/menu_icons.style index 261042d5c..dad37e892 100644 --- a/Telegram/SourceFiles/ui/menu_icons.style +++ b/Telegram/SourceFiles/ui/menu_icons.style @@ -107,6 +107,7 @@ menuIconSilent: icon {{ "menu/silent", menuIconColor }}; menuIconCustomize: icon {{ "menu/customize", menuIconColor }}; menuIconSoundOn: icon {{ "menu/sound_enable", menuIconColor }}; menuIconSoundOff: icon {{ "menu/sound_disable", menuIconColor }}; +menuIconSoundSelect: icon {{ "menu/sound_select", menuIconColor }}; menuIconTTLAny: icon {{ "menu/auto_delete_plain", menuIconColor }}; menuIconTTLAnyTextPosition: point(11px, 22px);