/* 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 "main/main_session_settings.h" #include "chat_helpers/tabbed_selector.h" #include "window/section_widget.h" #include "ui/widgets/input_fields.h" #include "support/support_common.h" #include "storage/serialize_common.h" #include "boxes/send_files_box.h" #include "core/application.h" #include "core/core_settings.h" #include "base/platform/base_platform_info.h" namespace Main { namespace { constexpr auto kLegacyCallsPeerToPeerNobody = 4; constexpr auto kVersionTag = -1; constexpr auto kVersion = 2; constexpr auto kMaxSavedPlaybackPositions = 16; } // namespace SessionSettings::SessionSettings() : _selectorTab(ChatHelpers::SelectorTab::Emoji) , _floatPlayerColumn(Window::Column::Second) , _floatPlayerCorner(RectPart::TopRight) , _dialogsWidthRatio(ThirdColumnByDefault() ? kDefaultBigDialogsWidthRatio : kDefaultDialogsWidthRatio) , _supportSwitch(Support::SwitchSettings::Next) { } bool SessionSettings::ThirdColumnByDefault() { return Platform::IsMacStoreBuild(); } QByteArray SessionSettings::serialize() const { const auto autoDownload = _autoDownload.serialize(); auto size = sizeof(qint32) * 38; size += _groupStickersSectionHidden.size() * sizeof(quint64); size += _mediaLastPlaybackPosition.size() * 2 * sizeof(quint64); size += Serialize::bytearraySize(autoDownload); size += sizeof(qint32) + _hiddenPinnedMessages.size() * (sizeof(quint64) + sizeof(qint32)); auto result = QByteArray(); result.reserve(size); { QDataStream stream(&result, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_5_1); stream << qint32(kVersionTag) << qint32(kVersion); stream << static_cast(_selectorTab); stream << qint32(_tabbedSelectorSectionEnabled ? 1 : 0); stream << qint32(_floatPlayerColumn); stream << qint32(_floatPlayerCorner); stream << qint32(_groupStickersSectionHidden.size()); for (auto peerId : _groupStickersSectionHidden) { stream << quint64(peerId); } stream << qint32(_thirdSectionInfoEnabled ? 1 : 0); stream << qint32(_smallDialogsList ? 1 : 0); stream << qint32(snap( qRound(_dialogsWidthRatio.current() * 1000000), 0, 1000000)); stream << qint32(_thirdColumnWidth.current()); stream << qint32(_thirdSectionExtendedBy); stream << qint32(_supportSwitch); stream << qint32(_supportFixChatsOrder ? 1 : 0); stream << qint32(_supportTemplatesAutocomplete ? 1 : 0); stream << qint32(_supportChatsTimeSlice.current()); stream << autoDownload; stream << qint32(_supportAllSearchResults.current() ? 1 : 0); stream << qint32(_archiveCollapsed.current() ? 1 : 0); stream << qint32(_archiveInMainMenu.current() ? 1 : 0); stream << qint32(_skipArchiveInSearch.current() ? 1 : 0); stream << qint32(_mediaLastPlaybackPosition.size()); for (const auto &[id, time] : _mediaLastPlaybackPosition) { stream << quint64(id) << qint64(time); } stream << qint32(_hiddenPinnedMessages.size()); for (const auto &[key, value] : _hiddenPinnedMessages) { stream << quint64(key) << qint32(value); } stream << qint32(_dialogsFiltersEnabled ? 1 : 0); } return result; } void SessionSettings::addFromSerialized(const QByteArray &serialized) { if (serialized.isEmpty()) { return; } auto &app = Core::App().settings(); QDataStream stream(serialized); stream.setVersion(QDataStream::Qt_5_1); qint32 versionTag = 0; qint32 version = 0; qint32 selectorTab = static_cast(ChatHelpers::SelectorTab::Emoji); qint32 appLastSeenWarningSeen = app.lastSeenWarningSeen() ? 1 : 0; qint32 tabbedSelectorSectionEnabled = 1; qint32 legacyTabbedSelectorSectionTooltipShown = 0; qint32 floatPlayerColumn = static_cast(Window::Column::Second); qint32 floatPlayerCorner = static_cast(RectPart::TopRight); base::flat_map appSoundOverrides; base::flat_set groupStickersSectionHidden; qint32 thirdSectionInfoEnabled = 0; qint32 smallDialogsList = 0; float64 dialogsWidthRatio = _dialogsWidthRatio.current(); int thirdColumnWidth = _thirdColumnWidth.current(); int thirdSectionExtendedBy = _thirdSectionExtendedBy; qint32 appSendFilesWay = static_cast(app.sendFilesWay()); qint32 legacyCallsPeerToPeer = qint32(0); qint32 appSendSubmitWay = static_cast(app.sendSubmitWay()); qint32 supportSwitch = static_cast(_supportSwitch); qint32 supportFixChatsOrder = _supportFixChatsOrder ? 1 : 0; qint32 supportTemplatesAutocomplete = _supportTemplatesAutocomplete ? 1 : 0; qint32 supportChatsTimeSlice = _supportChatsTimeSlice.current(); qint32 appIncludeMutedCounter = app.includeMutedCounter() ? 1 : 0; qint32 appCountUnreadMessages = app.countUnreadMessages() ? 1 : 0; qint32 appExeLaunchWarning = app.exeLaunchWarning() ? 1 : 0; QByteArray autoDownload; qint32 supportAllSearchResults = _supportAllSearchResults.current() ? 1 : 0; qint32 archiveCollapsed = _archiveCollapsed.current() ? 1 : 0; qint32 appNotifyAboutPinned = app.notifyAboutPinned() ? 1 : 0; qint32 archiveInMainMenu = _archiveInMainMenu.current() ? 1 : 0; qint32 skipArchiveInSearch = _skipArchiveInSearch.current() ? 1 : 0; qint32 legacyAutoplayGifs = 1; qint32 appLoopAnimatedStickers = app.loopAnimatedStickers() ? 1 : 0; qint32 appLargeEmoji = app.largeEmoji() ? 1 : 0; qint32 appReplaceEmoji = app.replaceEmoji() ? 1 : 0; qint32 appSuggestEmoji = app.suggestEmoji() ? 1 : 0; qint32 appSuggestStickersByEmoji = app.suggestStickersByEmoji() ? 1 : 0; qint32 appSpellcheckerEnabled = app.spellcheckerEnabled() ? 1 : 0; std::vector> mediaLastPlaybackPosition; qint32 appVideoPlaybackSpeed = Core::Settings::SerializePlaybackSpeed(app.videoPlaybackSpeed()); QByteArray appVideoPipGeometry = app.videoPipGeometry(); std::vector appDictionariesEnabled; qint32 appAutoDownloadDictionaries = app.autoDownloadDictionaries() ? 1 : 0; base::flat_map hiddenPinnedMessages; qint32 dialogsFiltersEnabled = _dialogsFiltersEnabled ? 1 : 0; stream >> versionTag; if (versionTag == kVersionTag) { stream >> version; stream >> selectorTab; } else { selectorTab = versionTag; } if (version < 2) { stream >> appLastSeenWarningSeen; } if (!stream.atEnd()) { stream >> tabbedSelectorSectionEnabled; } if (version < 2) { if (!stream.atEnd()) { auto count = qint32(0); stream >> count; if (stream.status() == QDataStream::Ok) { for (auto i = 0; i != count; ++i) { QString key, value; stream >> key >> value; appSoundOverrides.emplace(key, value); } } } if (!stream.atEnd()) { stream >> legacyTabbedSelectorSectionTooltipShown; } } if (!stream.atEnd()) { stream >> floatPlayerColumn >> floatPlayerCorner; } if (!stream.atEnd()) { auto count = qint32(0); stream >> count; if (stream.status() == QDataStream::Ok) { for (auto i = 0; i != count; ++i) { quint64 peerId; stream >> peerId; groupStickersSectionHidden.insert(peerId); } } } if (!stream.atEnd()) { stream >> thirdSectionInfoEnabled; stream >> smallDialogsList; } if (!stream.atEnd()) { qint32 value = 0; stream >> value; dialogsWidthRatio = snap(value / 1000000., 0., 1.); stream >> value; thirdColumnWidth = value; stream >> value; thirdSectionExtendedBy = value; } if (version < 2) { if (!stream.atEnd()) { stream >> appSendFilesWay; } if (!stream.atEnd()) { stream >> legacyCallsPeerToPeer; } } if (!stream.atEnd()) { if (version < 2) { stream >> appSendSubmitWay; } stream >> supportSwitch; stream >> supportFixChatsOrder; } if (!stream.atEnd()) { stream >> supportTemplatesAutocomplete; } if (!stream.atEnd()) { stream >> supportChatsTimeSlice; } if (version < 2) { if (!stream.atEnd()) { stream >> appIncludeMutedCounter; stream >> appCountUnreadMessages; } if (!stream.atEnd()) { stream >> appExeLaunchWarning; } } if (!stream.atEnd()) { stream >> autoDownload; } if (!stream.atEnd()) { stream >> supportAllSearchResults; } if (!stream.atEnd()) { stream >> archiveCollapsed; } if (version < 2) { if (!stream.atEnd()) { stream >> appNotifyAboutPinned; } } if (!stream.atEnd()) { stream >> archiveInMainMenu; } if (!stream.atEnd()) { stream >> skipArchiveInSearch; } if (version < 2) { if (!stream.atEnd()) { stream >> legacyAutoplayGifs; stream >> appLoopAnimatedStickers; stream >> appLargeEmoji; stream >> appReplaceEmoji; stream >> appSuggestEmoji; stream >> appSuggestStickersByEmoji; } if (!stream.atEnd()) { stream >> appSpellcheckerEnabled; } } if (!stream.atEnd()) { auto count = qint32(0); stream >> count; if (stream.status() == QDataStream::Ok) { for (auto i = 0; i != count; ++i) { quint64 documentId; qint64 time; stream >> documentId >> time; mediaLastPlaybackPosition.emplace_back(documentId, time); } } } if (version < 2) { if (!stream.atEnd()) { stream >> appVideoPlaybackSpeed; } if (!stream.atEnd()) { stream >> appVideoPipGeometry; } if (!stream.atEnd()) { auto count = qint32(0); stream >> count; if (stream.status() == QDataStream::Ok) { for (auto i = 0; i != count; ++i) { qint64 langId; stream >> langId; appDictionariesEnabled.emplace_back(langId); } } } if (!stream.atEnd()) { stream >> appAutoDownloadDictionaries; } } if (!stream.atEnd()) { auto count = qint32(0); stream >> count; if (stream.status() == QDataStream::Ok) { for (auto i = 0; i != count; ++i) { auto key = quint64(); auto value = qint32(); stream >> key >> value; hiddenPinnedMessages.emplace(key, value); } } } if (!stream.atEnd()) { stream >> dialogsFiltersEnabled; } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Main::SessionSettings::FromSerialized()")); return; } if (!autoDownload.isEmpty() && !_autoDownload.setFromSerialized(autoDownload)) { return; } if (!version) { if (!legacyAutoplayGifs) { using namespace Data::AutoDownload; _autoDownload = WithDisabledAutoPlay(_autoDownload); } } auto uncheckedTab = static_cast(selectorTab); switch (uncheckedTab) { case ChatHelpers::SelectorTab::Emoji: case ChatHelpers::SelectorTab::Stickers: case ChatHelpers::SelectorTab::Gifs: _selectorTab = uncheckedTab; break; } _tabbedSelectorSectionEnabled = (tabbedSelectorSectionEnabled == 1); auto uncheckedColumn = static_cast(floatPlayerColumn); switch (uncheckedColumn) { case Window::Column::First: case Window::Column::Second: case Window::Column::Third: _floatPlayerColumn = uncheckedColumn; break; } auto uncheckedCorner = static_cast(floatPlayerCorner); switch (uncheckedCorner) { case RectPart::TopLeft: case RectPart::TopRight: case RectPart::BottomLeft: case RectPart::BottomRight: _floatPlayerCorner = uncheckedCorner; break; } _groupStickersSectionHidden = std::move(groupStickersSectionHidden); _thirdSectionInfoEnabled = thirdSectionInfoEnabled; _smallDialogsList = smallDialogsList; _dialogsWidthRatio = dialogsWidthRatio; _thirdColumnWidth = thirdColumnWidth; _thirdSectionExtendedBy = thirdSectionExtendedBy; if (_thirdSectionInfoEnabled) { _tabbedSelectorSectionEnabled = false; } auto uncheckedSupportSwitch = static_cast( supportSwitch); switch (uncheckedSupportSwitch) { case Support::SwitchSettings::None: case Support::SwitchSettings::Next: case Support::SwitchSettings::Previous: _supportSwitch = uncheckedSupportSwitch; break; } _supportFixChatsOrder = (supportFixChatsOrder == 1); _supportTemplatesAutocomplete = (supportTemplatesAutocomplete == 1); _supportChatsTimeSlice = supportChatsTimeSlice; _hadLegacyCallsPeerToPeerNobody = (legacyCallsPeerToPeer == kLegacyCallsPeerToPeerNobody); _supportAllSearchResults = (supportAllSearchResults == 1); _archiveCollapsed = (archiveCollapsed == 1); _archiveInMainMenu = (archiveInMainMenu == 1); _skipArchiveInSearch = (skipArchiveInSearch == 1); _mediaLastPlaybackPosition = std::move(mediaLastPlaybackPosition); _hiddenPinnedMessages = std::move(hiddenPinnedMessages); _dialogsFiltersEnabled = (dialogsFiltersEnabled == 1); if (version < 2) { app.setLastSeenWarningSeen(appLastSeenWarningSeen == 1); for (const auto &[key, value] : appSoundOverrides) { app.setSoundOverride(key, value); } auto uncheckedSendFilesWay = static_cast(appSendFilesWay); switch (uncheckedSendFilesWay) { case SendFilesWay::Album: case SendFilesWay::Photos: case SendFilesWay::Files: app.setSendFilesWay(uncheckedSendFilesWay); break; } auto uncheckedSendSubmitWay = static_cast( appSendSubmitWay); switch (uncheckedSendSubmitWay) { case Ui::InputSubmitSettings::Enter: case Ui::InputSubmitSettings::CtrlEnter: app.setSendSubmitWay(uncheckedSendSubmitWay); break; } app.setIncludeMutedCounter(appIncludeMutedCounter == 1); app.setCountUnreadMessages(appCountUnreadMessages == 1); app.setExeLaunchWarning(appExeLaunchWarning == 1); app.setNotifyAboutPinned(appNotifyAboutPinned == 1); app.setLoopAnimatedStickers(appLoopAnimatedStickers == 1); app.setLargeEmoji(appLargeEmoji == 1); app.setReplaceEmoji(appReplaceEmoji == 1); app.setSuggestEmoji(appSuggestEmoji == 1); app.setSuggestStickersByEmoji(appSuggestStickersByEmoji == 1); app.setSpellcheckerEnabled(appSpellcheckerEnabled == 1); app.setVideoPlaybackSpeed(Core::Settings::DeserializePlaybackSpeed(appVideoPlaybackSpeed)); app.setVideoPipGeometry(appVideoPipGeometry); app.setDictionariesEnabled(std::move(appDictionariesEnabled)); app.setAutoDownloadDictionaries(appAutoDownloadDictionaries == 1); } } void SessionSettings::setSupportChatsTimeSlice(int slice) { _supportChatsTimeSlice = slice; } int SessionSettings::supportChatsTimeSlice() const { return _supportChatsTimeSlice.current(); } rpl::producer SessionSettings::supportChatsTimeSliceValue() const { return _supportChatsTimeSlice.value(); } void SessionSettings::setSupportAllSearchResults(bool all) { _supportAllSearchResults = all; } bool SessionSettings::supportAllSearchResults() const { return _supportAllSearchResults.current(); } rpl::producer SessionSettings::supportAllSearchResultsValue() const { return _supportAllSearchResults.value(); } void SessionSettings::setTabbedSelectorSectionEnabled(bool enabled) { _tabbedSelectorSectionEnabled = enabled; if (enabled) { setThirdSectionInfoEnabled(false); } setTabbedReplacedWithInfo(false); } rpl::producer SessionSettings::tabbedReplacedWithInfoValue() const { return _tabbedReplacedWithInfoValue.events_starting_with( tabbedReplacedWithInfo()); } void SessionSettings::setThirdSectionInfoEnabled(bool enabled) { if (_thirdSectionInfoEnabled != enabled) { _thirdSectionInfoEnabled = enabled; if (enabled) { setTabbedSelectorSectionEnabled(false); } setTabbedReplacedWithInfo(false); _thirdSectionInfoEnabledValue.fire_copy(enabled); } } rpl::producer SessionSettings::thirdSectionInfoEnabledValue() const { return _thirdSectionInfoEnabledValue.events_starting_with( thirdSectionInfoEnabled()); } void SessionSettings::setTabbedReplacedWithInfo(bool enabled) { if (_tabbedReplacedWithInfo != enabled) { _tabbedReplacedWithInfo = enabled; _tabbedReplacedWithInfoValue.fire_copy(enabled); } } void SessionSettings::setDialogsWidthRatio(float64 ratio) { _dialogsWidthRatio = ratio; } float64 SessionSettings::dialogsWidthRatio() const { return _dialogsWidthRatio.current(); } rpl::producer SessionSettings::dialogsWidthRatioChanges() const { return _dialogsWidthRatio.changes(); } void SessionSettings::setThirdColumnWidth(int width) { _thirdColumnWidth = width; } int SessionSettings::thirdColumnWidth() const { return _thirdColumnWidth.current(); } rpl::producer SessionSettings::thirdColumnWidthChanges() const { return _thirdColumnWidth.changes(); } void SessionSettings::setMediaLastPlaybackPosition(DocumentId id, crl::time time) { auto &map = _mediaLastPlaybackPosition; const auto i = ranges::find( map, id, &std::pair::first); if (i != map.end()) { if (time > 0) { i->second = time; } else { map.erase(i); } } else if (time > 0) { if (map.size() >= kMaxSavedPlaybackPositions) { map.erase(map.begin()); } map.emplace_back(id, time); } } crl::time SessionSettings::mediaLastPlaybackPosition(DocumentId id) const { const auto i = ranges::find( _mediaLastPlaybackPosition, id, &std::pair::first); return (i != _mediaLastPlaybackPosition.end()) ? i->second : 0; } void SessionSettings::setArchiveCollapsed(bool collapsed) { _archiveCollapsed = collapsed; } bool SessionSettings::archiveCollapsed() const { return _archiveCollapsed.current(); } rpl::producer SessionSettings::archiveCollapsedChanges() const { return _archiveCollapsed.changes(); } void SessionSettings::setArchiveInMainMenu(bool inMainMenu) { _archiveInMainMenu = inMainMenu; } bool SessionSettings::archiveInMainMenu() const { return _archiveInMainMenu.current(); } rpl::producer SessionSettings::archiveInMainMenuChanges() const { return _archiveInMainMenu.changes(); } void SessionSettings::setSkipArchiveInSearch(bool skip) { _skipArchiveInSearch = skip; } bool SessionSettings::skipArchiveInSearch() const { return _skipArchiveInSearch.current(); } rpl::producer SessionSettings::skipArchiveInSearchChanges() const { return _skipArchiveInSearch.changes(); } } // namespace Main