diff --git a/Telegram/SourceFiles/data/business/data_business_common.cpp b/Telegram/SourceFiles/data/business/data_business_common.cpp index 1de65c9521..956807f5d1 100644 --- a/Telegram/SourceFiles/data/business/data_business_common.cpp +++ b/Telegram/SourceFiles/data/business/data_business_common.cpp @@ -18,7 +18,7 @@ constexpr auto kInNextDayMax = WorkingInterval::kInNextDayMax; auto &list = intervals.list; ranges::sort(list, ranges::less(), &WorkingInterval::start); for (auto i = 0, count = int(list.size()); i != count; ++i) { - if (i && list[i].intersected(list[i - 1])) { + if (i && list[i] && list[i -1] && list[i].start <= list[i - 1].end) { list[i - 1] = list[i - 1].united(list[i]); list[i] = {}; } @@ -54,12 +54,12 @@ WorkingIntervals WorkingIntervals::normalized() const { return SortAndMerge(MoveTailToFront(SortAndMerge(*this))); } -Data::WorkingIntervals ExtractDayIntervals( - const Data::WorkingIntervals &intervals, +WorkingIntervals ExtractDayIntervals( + const WorkingIntervals &intervals, int dayIndex) { Expects(dayIndex >= 0 && dayIndex < 7); - auto result = Data::WorkingIntervals(); + auto result = WorkingIntervals(); auto &list = result.list; for (const auto &interval : intervals.list) { const auto now = interval.intersected( @@ -80,7 +80,7 @@ Data::WorkingIntervals ExtractDayIntervals( } result = result.normalized(); - const auto outside = [&](Data::WorkingInterval interval) { + const auto outside = [&](WorkingInterval interval) { return (interval.end <= 0) || (interval.start >= kDay); }; list.erase(ranges::remove_if(list, outside), end(list)); @@ -106,15 +106,15 @@ Data::WorkingIntervals ExtractDayIntervals( return result; } -Data::WorkingIntervals RemoveDayIntervals( - const Data::WorkingIntervals &intervals, +WorkingIntervals RemoveDayIntervals( + const WorkingIntervals &intervals, int dayIndex) { auto result = intervals.normalized(); auto &list = result.list; - const auto day = Data::WorkingInterval{ 0, kDay }; + const auto day = WorkingInterval{ 0, kDay }; const auto shifted = day.shifted(dayIndex * kDay); - auto before = Data::WorkingInterval{ 0, shifted.start }; - auto after = Data::WorkingInterval{ shifted.end, kWeek }; + auto before = WorkingInterval{ 0, shifted.start }; + auto after = WorkingInterval{ shifted.end, kWeek }; for (auto i = 0, count = int(list.size()); i != count; ++i) { if (list[i].end <= shifted.start || list[i].start >= shifted.end) { continue; @@ -140,10 +140,10 @@ Data::WorkingIntervals RemoveDayIntervals( return result.normalized(); } -Data::WorkingIntervals ReplaceDayIntervals( - const Data::WorkingIntervals &intervals, +WorkingIntervals ReplaceDayIntervals( + const WorkingIntervals &intervals, int dayIndex, - Data::WorkingIntervals replacement) { + WorkingIntervals replacement) { auto result = RemoveDayIntervals(intervals, dayIndex); const auto first = result.list.insert( end(result.list), diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h index 41fcca431f..be281d0ad2 100644 --- a/Telegram/SourceFiles/data/business/data_business_common.h +++ b/Telegram/SourceFiles/data/business/data_business_common.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "base/flags.h" +#include "data/data_location.h" class UserData; @@ -125,20 +126,50 @@ struct WorkingHours { return { intervals.normalized(), timezoneId }; } + explicit operator bool() const { + return !timezoneId.isEmpty(); + } + friend inline bool operator==( const WorkingHours &a, const WorkingHours &b) = default; }; -[[nodiscard]] Data::WorkingIntervals ExtractDayIntervals( - const Data::WorkingIntervals &intervals, +[[nodiscard]] WorkingIntervals ExtractDayIntervals( + const WorkingIntervals &intervals, int dayIndex); -[[nodiscard]] Data::WorkingIntervals RemoveDayIntervals( - const Data::WorkingIntervals &intervals, +[[nodiscard]] WorkingIntervals RemoveDayIntervals( + const WorkingIntervals &intervals, int dayIndex); -[[nodiscard]] Data::WorkingIntervals ReplaceDayIntervals( - const Data::WorkingIntervals &intervals, +[[nodiscard]] WorkingIntervals ReplaceDayIntervals( + const WorkingIntervals &intervals, int dayIndex, - Data::WorkingIntervals replacement); + WorkingIntervals replacement); + +struct BusinessLocation { + QString address; + LocationPoint point; + + explicit operator bool() const { + return !address.isEmpty(); + } + + friend inline bool operator==( + const BusinessLocation &a, + const BusinessLocation &b) = default; +}; + +struct BusinessDetails { + WorkingHours hours; + BusinessLocation location; + + explicit operator bool() const { + return hours || location; + } + + friend inline bool operator==( + const BusinessDetails &a, + const BusinessDetails &b) = default; +}; } // namespace Data diff --git a/Telegram/SourceFiles/data/business/data_business_info.cpp b/Telegram/SourceFiles/data/business/data_business_info.cpp index c4623bb1f9..804b23debd 100644 --- a/Telegram/SourceFiles/data/business/data_business_info.cpp +++ b/Telegram/SourceFiles/data/business/data_business_info.cpp @@ -8,10 +8,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/business/data_business_info.h" #include "apiwrap.h" +#include "data/business/data_business_common.h" #include "data/data_session.h" +#include "data/data_user.h" #include "main/main_session.h" namespace Data { +namespace { + +[[nodiscard]] MTPBusinessWorkHours ToMTP(const WorkingHours &data) { + const auto list = data.intervals.normalized().list; + const auto proj = [](const WorkingInterval &data) { + return MTPBusinessWeeklyOpen(MTP_businessWeeklyOpen( + MTP_int(data.start / 60), + MTP_int(data.end / 60))); + }; + return MTP_businessWorkHours( + MTP_flags(0), + MTP_string(data.timezoneId), + MTP_vector_from_range(list | ranges::views::transform(proj))); +} + +} // namespace BusinessInfo::BusinessInfo(not_null owner) : _owner(owner) { @@ -19,16 +37,20 @@ BusinessInfo::BusinessInfo(not_null owner) BusinessInfo::~BusinessInfo() = default; -const WorkingHours &BusinessInfo::workingHours() const { - return _workingHours.current(); -} - -rpl::producer BusinessInfo::workingHoursValue() const { - return _workingHours.value(); -} - void BusinessInfo::saveWorkingHours(WorkingHours data) { - _workingHours = std::move(data); + auto details = _owner->session().user()->businessDetails(); + if (details.hours == data) { + return; + } + details.hours = std::move(data); + + using Flag = MTPaccount_UpdateBusinessWorkHours::Flag; + _owner->session().api().request(MTPaccount_UpdateBusinessWorkHours( + MTP_flags(details.hours ? Flag::f_business_work_hours : Flag()), + ToMTP(details.hours) + )).send(); + + _owner->session().user()->setBusinessDetails(std::move(details)); } void BusinessInfo::preload() { diff --git a/Telegram/SourceFiles/data/business/data_business_info.h b/Telegram/SourceFiles/data/business/data_business_info.h index f109165d70..ee4a2e0431 100644 --- a/Telegram/SourceFiles/data/business/data_business_info.h +++ b/Telegram/SourceFiles/data/business/data_business_info.h @@ -18,8 +18,6 @@ public: explicit BusinessInfo(not_null owner); ~BusinessInfo(); - [[nodiscard]] const WorkingHours &workingHours() const; - [[nodiscard]] rpl::producer workingHoursValue() const; void saveWorkingHours(WorkingHours data); void preload(); @@ -29,8 +27,6 @@ public: private: const not_null _owner; - rpl::variable _workingHours; - rpl::variable _timezones; mtpRequestId _timezonesRequestId = 0; diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h index 2c29ef8fca..bd85b02faa 100644 --- a/Telegram/SourceFiles/data/data_changes.h +++ b/Telegram/SourceFiles/data/data_changes.h @@ -73,42 +73,43 @@ struct PeerUpdate { TranslationDisabled = (1ULL << 13), Color = (1ULL << 14), BackgroundEmoji = (1ULL << 15), + StoriesState = (1ULL << 16), // For users - CanShareContact = (1ULL << 16), - IsContact = (1ULL << 17), - PhoneNumber = (1ULL << 18), - OnlineStatus = (1ULL << 19), - BotCommands = (1ULL << 20), - BotCanBeInvited = (1ULL << 21), - BotStartToken = (1ULL << 22), - CommonChats = (1ULL << 23), - HasCalls = (1ULL << 24), - SupportInfo = (1ULL << 25), - IsBot = (1ULL << 26), - EmojiStatus = (1ULL << 27), - StoriesState = (1ULL << 28), + CanShareContact = (1ULL << 17), + IsContact = (1ULL << 18), + PhoneNumber = (1ULL << 19), + OnlineStatus = (1ULL << 20), + BotCommands = (1ULL << 21), + BotCanBeInvited = (1ULL << 22), + BotStartToken = (1ULL << 23), + CommonChats = (1ULL << 24), + HasCalls = (1ULL << 25), + SupportInfo = (1ULL << 26), + IsBot = (1ULL << 27), + EmojiStatus = (1ULL << 28), + BusinessDetails = (1ULL << 29), // For chats and channels - InviteLinks = (1ULL << 29), - Members = (1ULL << 30), - Admins = (1ULL << 31), - BannedUsers = (1ULL << 32), - Rights = (1ULL << 33), - PendingRequests = (1ULL << 34), - Reactions = (1ULL << 35), + InviteLinks = (1ULL << 30), + Members = (1ULL << 31), + Admins = (1ULL << 32), + BannedUsers = (1ULL << 33), + Rights = (1ULL << 34), + PendingRequests = (1ULL << 35), + Reactions = (1ULL << 36), // For channels - ChannelAmIn = (1ULL << 36), - StickersSet = (1ULL << 37), - EmojiSet = (1ULL << 38), - ChannelLinkedChat = (1ULL << 39), - ChannelLocation = (1ULL << 40), - Slowmode = (1ULL << 41), - GroupCall = (1ULL << 42), + ChannelAmIn = (1ULL << 37), + StickersSet = (1ULL << 38), + EmojiSet = (1ULL << 39), + ChannelLinkedChat = (1ULL << 40), + ChannelLocation = (1ULL << 41), + Slowmode = (1ULL << 42), + GroupCall = (1ULL << 43), // For iteration - LastUsedBit = (1ULL << 42), + LastUsedBit = (1ULL << 43), }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; } diff --git a/Telegram/SourceFiles/data/data_location.h b/Telegram/SourceFiles/data/data_location.h index 7d9a59b4a7..a5e0090db8 100644 --- a/Telegram/SourceFiles/data/data_location.h +++ b/Telegram/SourceFiles/data/data_location.h @@ -26,7 +26,6 @@ public: [[nodiscard]] size_t hash() const; -private: friend inline bool operator==( const LocationPoint &a, const LocationPoint &b) { @@ -39,6 +38,7 @@ private: return (a._lat < b._lat) || ((a._lat == b._lat) && (a._lon < b._lon)); } +private: float64 _lat = 0; float64 _lon = 0; uint64 _access = 0; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 5014bdfb9e..45f4f9e28a 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "storage/storage_user_photos.h" #include "main/main_session.h" +#include "data/business/data_business_common.h" #include "data/data_session.h" #include "data/data_changes.h" #include "data/data_peer_bot_command.h" @@ -30,6 +31,34 @@ constexpr auto kSetOnlineAfterActivity = TimeId(30); using UpdateFlag = Data::PeerUpdate::Flag; +[[nodiscard]] Data::BusinessDetails FromMTP( + const tl::conditional &hours, + const tl::conditional &location) { + auto result = Data::BusinessDetails(); + if (hours) { + const auto &data = hours->data(); + result.hours.timezoneId = qs(data.vtimezone_id()); + result.hours.intervals.list = ranges::views::all( + data.vweekly_open().v + ) | ranges::views::transform([](const MTPBusinessWeeklyOpen &open) { + const auto &data = open.data(); + return Data::WorkingInterval{ + data.vstart_minute().v * 60, + data.vend_minute().v * 60, + }; + }) | ranges::to_vector; + } + if (location) { + const auto &data = location->data(); + result.location.address = qs(data.vaddress()); + data.vgeo_point().match([&](const MTPDgeoPoint &data) { + result.location.point = Data::LocationPoint(data); + }, [&](const MTPDgeoPointEmpty &) { + }); + } + return result; +} + } // namespace BotInfo::BotInfo() = default; @@ -62,6 +91,8 @@ UserData::UserData(not_null owner, PeerId id) , _flags((id == owner->session().userPeerId()) ? Flag::Self : Flag(0)) { } +UserData::~UserData() = default; + bool UserData::canShareThisContact() const { return canShareThisContactFast() || !owner().findContactPhone(peerToUser(id)).isEmpty(); @@ -174,6 +205,22 @@ void UserData::setStoriesState(StoriesState state) { } } +const Data::BusinessDetails &UserData::businessDetails() const { + static const auto empty = Data::BusinessDetails(); + return _businessDetails ? *_businessDetails : empty; +} + +void UserData::setBusinessDetails(Data::BusinessDetails details) { + if ((!details && !_businessDetails) + || (details && _businessDetails && details == *_businessDetails)) { + return; + } + _businessDetails = details + ? std::make_unique(std::move(details)) + : nullptr; + session().changes().peerUpdated(this, UpdateFlag::BusinessDetails); +} + void UserData::setName(const QString &newFirstName, const QString &newLastName, const QString &newPhoneName, const QString &newUsername) { bool changeName = !newFirstName.isEmpty() || !newLastName.isEmpty(); @@ -572,6 +619,10 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { user->setWallPaper({}); } + user->setBusinessDetails(FromMTP( + update.vbusiness_work_hours(), + update.vbusiness_location())); + user->owner().stories().apply(user, update.vstories()); user->fullUpdated(); diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index f6ba397496..cf9eafef70 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { struct BotCommand; +struct BusinessDetails; } // namespace Data struct BotInfo { @@ -84,6 +85,8 @@ public: using Flags = Data::Flags; UserData(not_null owner, PeerId id); + ~UserData(); + void setPhoto(const MTPUserProfilePhoto &photo); void setName( @@ -192,6 +195,9 @@ public: [[nodiscard]] bool hasUnreadStories() const; void setStoriesState(StoriesState state); + [[nodiscard]] const Data::BusinessDetails &businessDetails() const; + void setBusinessDetails(Data::BusinessDetails details); + private: auto unavailableReasons() const -> const std::vector & override; @@ -201,6 +207,7 @@ private: Data::UsernamesInfo _username; + std::unique_ptr _businessDetails; std::vector _unavailableReasons; QString _phone; QString _privateForwardName; diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp index fbe4ccc607..42865a2c07 100644 --- a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp +++ b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "data/business/data_business_info.h" #include "data/data_session.h" +#include "data/data_user.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "settings/business/settings_recipients_helper.h" @@ -50,6 +51,7 @@ private: void save(); rpl::variable _hours; + rpl::variable _enabled; }; @@ -566,7 +568,7 @@ void WorkingHours::setupContent( const auto state = content->lifetime().make_state(State{ .timezones = info->timezonesValue(), }); - _hours = info->workingHours(); + _hours = controller->session().user()->businessDetails().hours; AddDividerTextWithLottie(content, { .lottie = u"hours"_q, @@ -582,7 +584,9 @@ void WorkingHours::setupContent( content, tr::lng_hours_show(), st::settingsButtonNoIcon - ))->toggleOn(rpl::single(false)); + ))->toggleOn(rpl::single(bool(_hours.current()))); + + _enabled = enabled->toggledValue(); const auto wrap = content->add( object_ptr>( @@ -670,7 +674,7 @@ void WorkingHours::setupContent( void WorkingHours::save() { controller()->session().data().businessInfo().saveWorkingHours( - _hours.current()); + _enabled.current() ? _hours.current() : Data::WorkingHours()); } } // namespace