Support business working hours API.

This commit is contained in:
John Preston 2024-02-22 22:00:11 +04:00
parent 4d12f1c0ef
commit dd0bdd62fb
9 changed files with 177 additions and 65 deletions

View file

@ -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),

View file

@ -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

View file

@ -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<Session*> owner)
: _owner(owner) {
@ -19,16 +37,20 @@ BusinessInfo::BusinessInfo(not_null<Session*> owner)
BusinessInfo::~BusinessInfo() = default;
const WorkingHours &BusinessInfo::workingHours() const {
return _workingHours.current();
}
rpl::producer<WorkingHours> 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() {

View file

@ -18,8 +18,6 @@ public:
explicit BusinessInfo(not_null<Session*> owner);
~BusinessInfo();
[[nodiscard]] const WorkingHours &workingHours() const;
[[nodiscard]] rpl::producer<WorkingHours> workingHoursValue() const;
void saveWorkingHours(WorkingHours data);
void preload();
@ -29,8 +27,6 @@ public:
private:
const not_null<Session*> _owner;
rpl::variable<WorkingHours> _workingHours;
rpl::variable<Timezones> _timezones;
mtpRequestId _timezonesRequestId = 0;

View file

@ -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<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; }

View file

@ -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;

View file

@ -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<MTPBusinessWorkHours> &hours,
const tl::conditional<MTPBusinessLocation> &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<Data::Session*> 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<Data::BusinessDetails>(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<UserData*> 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();

View file

@ -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<UserDataFlags>;
UserData(not_null<Data::Session*> 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<Data::UnavailableReason> & override;
@ -201,6 +207,7 @@ private:
Data::UsernamesInfo _username;
std::unique_ptr<Data::BusinessDetails> _businessDetails;
std::vector<Data::UnavailableReason> _unavailableReasons;
QString _phone;
QString _privateForwardName;

View file

@ -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<Data::WorkingHours> _hours;
rpl::variable<bool> _enabled;
};
@ -566,7 +568,7 @@ void WorkingHours::setupContent(
const auto state = content->lifetime().make_state<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<Ui::SlideWrap<Ui::VerticalLayout>>(
@ -670,7 +674,7 @@ void WorkingHours::setupContent(
void WorkingHours::save() {
controller()->session().data().businessInfo().saveWorkingHours(
_hours.current());
_enabled.current() ? _hours.current() : Data::WorkingHours());
}
} // namespace