mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Show location and working hours in profile.
This commit is contained in:
parent
5e82860376
commit
ea36345eee
9 changed files with 525 additions and 26 deletions
|
@ -1313,6 +1313,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_info_link_label" = "Link";
|
||||
"lng_info_location_label" = "Location";
|
||||
"lng_info_about_label" = "About";
|
||||
"lng_info_work_open" = "Open";
|
||||
"lng_info_work_closed" = "Closed";
|
||||
"lng_info_hours_label" = "Business hours";
|
||||
"lng_info_hours_closed" = "closed";
|
||||
"lng_info_hours_opens_in_minutes#one" = "opens in {count} minute";
|
||||
"lng_info_hours_opens_in_minutes#other" = "opens in {count} minutes";
|
||||
"lng_info_hours_opens_in_hours#one" = "opens in {count} hour";
|
||||
"lng_info_hours_opens_in_hours#other" = "opens in {count} hours";
|
||||
"lng_info_hours_opens_in_days#one" = "opens in {count} day";
|
||||
"lng_info_hours_opens_in_days#other" = "opens in {count} days";
|
||||
"lng_info_hours_open_full" = "open 24 hours";
|
||||
"lng_info_hours_next_day" = "{time} (next day)";
|
||||
"lng_info_hours_local_time" = "local time";
|
||||
"lng_info_hours_my_time" = "my time";
|
||||
"lng_info_user_title" = "User Info";
|
||||
"lng_info_bot_title" = "Bot Info";
|
||||
"lng_info_group_title" = "Group Info";
|
||||
|
@ -2190,7 +2204,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_hours_sunday" = "Sunday";
|
||||
"lng_hours_closed" = "Closed";
|
||||
"lng_hours_open_full" = "Open 24 hours";
|
||||
"lng_hours_next_day" = "Next day, {time}";
|
||||
"lng_hours_next_day" = "{time} (Next day)";
|
||||
"lng_hours_time_zone_title" = "Choose Time Zone";
|
||||
"lng_hours_add_button" = "Add a Set of Hours";
|
||||
"lng_hours_opening" = "Opening Time";
|
||||
|
|
|
@ -106,6 +106,11 @@ WorkingIntervals ExtractDayIntervals(
|
|||
return result;
|
||||
}
|
||||
|
||||
bool IsFullOpen(const WorkingIntervals &extractedDay) {
|
||||
return extractedDay
|
||||
&& (extractedDay.list.front() == WorkingInterval{ 0, kDay });
|
||||
}
|
||||
|
||||
WorkingIntervals RemoveDayIntervals(
|
||||
const WorkingIntervals &intervals,
|
||||
int dayIndex) {
|
||||
|
|
|
@ -138,6 +138,7 @@ struct WorkingHours {
|
|||
[[nodiscard]] WorkingIntervals ExtractDayIntervals(
|
||||
const WorkingIntervals &intervals,
|
||||
int dayIndex);
|
||||
[[nodiscard]] bool IsFullOpen(const WorkingIntervals &extractedDay);
|
||||
[[nodiscard]] WorkingIntervals RemoveDayIntervals(
|
||||
const WorkingIntervals &intervals,
|
||||
int dayIndex);
|
||||
|
@ -148,7 +149,7 @@ struct WorkingHours {
|
|||
|
||||
struct BusinessLocation {
|
||||
QString address;
|
||||
LocationPoint point;
|
||||
std::optional<LocationPoint> point;
|
||||
|
||||
explicit operator bool() const {
|
||||
return !address.isEmpty();
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/business/data_business_info.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "data/business/data_business_common.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
|
@ -270,4 +271,24 @@ rpl::producer<Timezones> BusinessInfo::timezonesValue() const {
|
|||
return _timezones.value();
|
||||
}
|
||||
|
||||
QString FindClosestTimezoneId(const std::vector<Timezone> &list) {
|
||||
const auto local = QDateTime::currentDateTime();
|
||||
const auto utc = QDateTime(local.date(), local.time(), Qt::UTC);
|
||||
const auto shift = base::unixtime::now() - (TimeId)::time(nullptr);
|
||||
const auto delta = int(utc.toSecsSinceEpoch())
|
||||
- int(local.toSecsSinceEpoch())
|
||||
- shift;
|
||||
const auto proj = [&](const Timezone &value) {
|
||||
auto distance = value.utcOffset - delta;
|
||||
while (distance > 12 * 3600) {
|
||||
distance -= 24 * 3600;
|
||||
}
|
||||
while (distance < -12 * 3600) {
|
||||
distance += 24 * 3600;
|
||||
}
|
||||
return std::abs(distance);
|
||||
};
|
||||
return ranges::min_element(list, ranges::less(), proj)->id;
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -55,4 +55,7 @@ private:
|
|||
|
||||
};
|
||||
|
||||
[[nodiscard]] QString FindClosestTimezoneId(
|
||||
const std::vector<Timezone> &list);
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -280,6 +280,7 @@ const Data::BusinessDetails &UserData::businessDetails() const {
|
|||
}
|
||||
|
||||
void UserData::setBusinessDetails(Data::BusinessDetails details) {
|
||||
details.hours = details.hours.normalized();
|
||||
if ((!details && !_businessDetails)
|
||||
|| (details && _businessDetails && details == *_businessDetails)) {
|
||||
return;
|
||||
|
|
|
@ -1008,3 +1008,21 @@ similarChannelsLockAbout: FlatLabel(defaultFlatLabel) {
|
|||
minWidth: 128px;
|
||||
}
|
||||
similarChannelsLockAboutPadding: margins(12px, 12px, 12px, 12px);
|
||||
|
||||
infoHoursState: FlatLabel(infoLabeled) {
|
||||
minWidth: 0px;
|
||||
}
|
||||
infoHoursValue: FlatLabel(infoHoursState) {
|
||||
textFg: windowSubTextFg;
|
||||
align: align(topright);
|
||||
}
|
||||
infoHoursDayLabel: infoHoursState;
|
||||
infoHoursOuter: RoundButton(defaultActiveButton) {
|
||||
textBg: transparent;
|
||||
textBgOver: transparent;
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgOver;
|
||||
}
|
||||
}
|
||||
infoHoursOuterMargin: margins(8px, 4px, 8px, 4px);
|
||||
infoHoursDaySkip: 6px;
|
||||
|
|
|
@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "api/api_chat_participants.h"
|
||||
#include "base/options.h"
|
||||
#include "base/timer_rpl.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "data/business/data_business_common.h"
|
||||
#include "data/business/data_business_info.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_folder.h"
|
||||
|
@ -71,6 +75,8 @@ namespace Info {
|
|||
namespace Profile {
|
||||
namespace {
|
||||
|
||||
constexpr auto kDay = Data::WorkingInterval::kDay;
|
||||
|
||||
base::options::toggle ShowPeerIdBelowAbout({
|
||||
.id = kOptionShowPeerIdBelowAbout,
|
||||
.name = "Show Peer IDs in Profile",
|
||||
|
@ -159,6 +165,435 @@ base::options::toggle ShowPeerIdBelowAbout({
|
|||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] bool AreNonTrivialHours(const Data::WorkingHours &hours) {
|
||||
if (!hours) {
|
||||
return false;
|
||||
}
|
||||
const auto &intervals = hours.intervals.list;
|
||||
for (auto i = 0; i != 7; ++i) {
|
||||
const auto day = Data::WorkingInterval{ i * kDay, (i + 1) * kDay };
|
||||
for (const auto &interval : intervals) {
|
||||
const auto intersection = interval.intersected(day);
|
||||
if (intersection && intersection != day) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] TimeId OpensIn(
|
||||
const Data::WorkingIntervals &intervals,
|
||||
TimeId now) {
|
||||
using namespace Data;
|
||||
|
||||
while (now < 0) {
|
||||
now += WorkingInterval::kWeek;
|
||||
}
|
||||
while (now > WorkingInterval::kWeek) {
|
||||
now -= WorkingInterval::kWeek;
|
||||
}
|
||||
auto closest = WorkingInterval::kWeek;
|
||||
for (const auto &interval : intervals.list) {
|
||||
if (interval.start <= now && interval.end > now) {
|
||||
return TimeId(0);
|
||||
} else if (interval.start > now && interval.start - now < closest) {
|
||||
closest = interval.start - now;
|
||||
} else if (interval.start < now) {
|
||||
const auto next = interval.start + WorkingInterval::kWeek - now;
|
||||
if (next < closest) {
|
||||
closest = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return closest;
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<QString> OpensInText(
|
||||
rpl::producer<TimeId> in,
|
||||
rpl::producer<QString> fallback) {
|
||||
return rpl::combine(
|
||||
std::move(in),
|
||||
std::move(fallback)
|
||||
) | rpl::map([](TimeId in, QString fallback) {
|
||||
return !in
|
||||
? std::move(fallback)
|
||||
: (in >= 86400)
|
||||
? tr::lng_info_hours_opens_in_days(tr::now, lt_count, in / 86400)
|
||||
: (in >= 3600)
|
||||
? tr::lng_info_hours_opens_in_hours(tr::now, lt_count, in / 3600)
|
||||
: tr::lng_info_hours_opens_in_minutes(
|
||||
tr::now,
|
||||
lt_count,
|
||||
std::max(in / 60, 1));
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] QString FormatDayTime(TimeId time) {
|
||||
const auto wrap = [](TimeId value) {
|
||||
const auto hours = value / 3600;
|
||||
const auto minutes = (value % 3600) / 60;
|
||||
return QString::number(hours).rightJustified(2, u'0')
|
||||
+ ':'
|
||||
+ QString::number(minutes).rightJustified(2, u'0');
|
||||
};
|
||||
return (time > kDay)
|
||||
? tr::lng_info_hours_next_day(tr::now, lt_time, wrap(time - kDay))
|
||||
: wrap(time == kDay ? 0 : time);
|
||||
}
|
||||
|
||||
[[nodiscard]] QString JoinIntervals(const Data::WorkingIntervals &data) {
|
||||
auto result = QStringList();
|
||||
result.reserve(data.list.size());
|
||||
for (const auto &interval : data.list) {
|
||||
const auto start = FormatDayTime(interval.start);
|
||||
const auto end = FormatDayTime(interval.end);
|
||||
result.push_back(start + u" - "_q + end);
|
||||
}
|
||||
return result.join('\n');
|
||||
}
|
||||
|
||||
[[nodiscard]] QString FormatDayHours(
|
||||
const Data::WorkingHours &hours,
|
||||
const Data::WorkingIntervals &mine,
|
||||
bool my,
|
||||
int day) {
|
||||
using namespace Data;
|
||||
|
||||
const auto local = ExtractDayIntervals(hours.intervals, day);
|
||||
if (IsFullOpen(local)) {
|
||||
return tr::lng_info_hours_open_full(tr::now);
|
||||
}
|
||||
const auto use = my ? ExtractDayIntervals(mine, day) : local;
|
||||
if (!use) {
|
||||
return tr::lng_info_hours_closed(tr::now);
|
||||
}
|
||||
return JoinIntervals(use);
|
||||
}
|
||||
|
||||
[[nodiscard]] Data::WorkingIntervals ShiftedIntervals(
|
||||
Data::WorkingIntervals intervals,
|
||||
int delta) {
|
||||
auto &list = intervals.list;
|
||||
if (!delta || list.empty()) {
|
||||
return { std::move(list) };
|
||||
}
|
||||
for (auto &interval : list) {
|
||||
interval.start += delta;
|
||||
interval.end += delta;
|
||||
}
|
||||
while (list.front().start < 0) {
|
||||
constexpr auto kWeek = Data::WorkingInterval::kWeek;
|
||||
const auto first = list.front();
|
||||
if (first.end > 0) {
|
||||
list.push_back({ first.start + kWeek, kWeek });
|
||||
list.front().start = 0;
|
||||
} else {
|
||||
list.push_back(first.shifted(kWeek));
|
||||
list.erase(list.begin());
|
||||
}
|
||||
}
|
||||
return intervals.normalized();
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::SlideWrap<>> CreateWorkingHours(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<UserData*> user) {
|
||||
using namespace Data;
|
||||
|
||||
auto result = object_ptr<Ui::SlideWrap<Ui::RoundButton>>(
|
||||
parent,
|
||||
object_ptr<Ui::RoundButton>(
|
||||
parent,
|
||||
rpl::single(QString()),
|
||||
st::infoHoursOuter),
|
||||
st::infoProfileLabeledPadding - st::infoHoursOuterMargin);
|
||||
const auto button = result->entity();
|
||||
const auto inner = Ui::CreateChild<Ui::VerticalLayout>(button);
|
||||
button->widthValue() | rpl::start_with_next([=](int width) {
|
||||
const auto margin = st::infoHoursOuterMargin;
|
||||
inner->resizeToWidth(width - margin.left() - margin.right());
|
||||
inner->move(margin.left(), margin.top());
|
||||
}, inner->lifetime());
|
||||
inner->heightValue() | rpl::start_with_next([=](int height) {
|
||||
const auto margin = st::infoHoursOuterMargin;
|
||||
height += margin.top() + margin.bottom();
|
||||
button->resize(button->width(), height);
|
||||
}, inner->lifetime());
|
||||
|
||||
const auto info = &user->owner().businessInfo();
|
||||
|
||||
struct State {
|
||||
rpl::variable<WorkingHours> hours;
|
||||
rpl::variable<TimeId> time;
|
||||
rpl::variable<int> day;
|
||||
rpl::variable<int> timezoneDelta;
|
||||
|
||||
rpl::variable<WorkingIntervals> mine;
|
||||
rpl::variable<WorkingIntervals> mineByDays;
|
||||
rpl::variable<TimeId> opensIn;
|
||||
rpl::variable<bool> opened;
|
||||
rpl::variable<bool> expanded;
|
||||
rpl::variable<bool> nonTrivial;
|
||||
rpl::variable<bool> myTimezone;
|
||||
|
||||
rpl::event_stream<> recounts;
|
||||
};
|
||||
const auto state = inner->lifetime().make_state<State>();
|
||||
|
||||
auto recounts = state->recounts.events_starting_with_copy(rpl::empty);
|
||||
const auto recount = [=] {
|
||||
state->recounts.fire({});
|
||||
};
|
||||
|
||||
state->hours = user->session().changes().peerFlagsValue(
|
||||
user,
|
||||
PeerUpdate::Flag::BusinessDetails
|
||||
) | rpl::map([=] {
|
||||
return user->businessDetails().hours;
|
||||
});
|
||||
state->nonTrivial = state->hours.value() | rpl::map(AreNonTrivialHours);
|
||||
|
||||
const auto seconds = QTime::currentTime().msecsSinceStartOfDay() / 1000;
|
||||
const auto inMinute = seconds % 60;
|
||||
const auto firstTick = inMinute ? (61 - inMinute) : 1;
|
||||
state->time = rpl::single(rpl::empty) | rpl::then(
|
||||
base::timer_once(firstTick * crl::time(1000))
|
||||
) | rpl::then(
|
||||
base::timer_each(60 * crl::time(1000))
|
||||
) | rpl::map([] {
|
||||
const auto local = QDateTime::currentDateTime();
|
||||
const auto day = local.date().dayOfWeek() - 1;
|
||||
const auto seconds = local.time().msecsSinceStartOfDay() / 1000;
|
||||
return day * kDay + seconds;
|
||||
});
|
||||
|
||||
state->day = state->time.value() | rpl::map([](TimeId time) {
|
||||
return time / kDay;
|
||||
});
|
||||
state->timezoneDelta = rpl::combine(
|
||||
state->hours.value(),
|
||||
info->timezonesValue()
|
||||
) | rpl::filter([](
|
||||
const WorkingHours &hours,
|
||||
const Timezones &timezones) {
|
||||
return ranges::contains(
|
||||
timezones.list,
|
||||
hours.timezoneId,
|
||||
&Timezone::id);
|
||||
}) | rpl::map([](WorkingHours &&hours, const Timezones &timezones) {
|
||||
const auto &list = timezones.list;
|
||||
const auto closest = FindClosestTimezoneId(list);
|
||||
const auto i = ranges::find(list, closest, &Timezone::id);
|
||||
const auto j = ranges::find(list, hours.timezoneId, &Timezone::id);
|
||||
Assert(i != end(list));
|
||||
Assert(j != end(list));
|
||||
return i->utcOffset - j->utcOffset;
|
||||
});
|
||||
|
||||
state->mine = rpl::combine(
|
||||
state->hours.value(),
|
||||
state->timezoneDelta.value()
|
||||
) | rpl::map([](WorkingHours &&hours, int delta) {
|
||||
return ShiftedIntervals(hours.intervals, delta);
|
||||
});
|
||||
|
||||
state->opensIn = rpl::combine(
|
||||
state->mine.value(),
|
||||
state->time.value()
|
||||
) | rpl::map([](const WorkingIntervals &mine, TimeId time) {
|
||||
return OpensIn(mine, time);
|
||||
});
|
||||
state->opened = state->opensIn.value() | rpl::map(rpl::mappers::_1 == 0);
|
||||
|
||||
state->mineByDays = rpl::combine(
|
||||
state->hours.value(),
|
||||
state->timezoneDelta.value()
|
||||
) | rpl::map([](WorkingHours &&hours, int delta) {
|
||||
auto full = std::array<bool, 7>();
|
||||
auto withoutFullDays = hours.intervals;
|
||||
for (auto i = 0; i != 7; ++i) {
|
||||
if (IsFullOpen(ExtractDayIntervals(hours.intervals, i))) {
|
||||
full[i] = true;
|
||||
withoutFullDays = ReplaceDayIntervals(
|
||||
withoutFullDays,
|
||||
i,
|
||||
Data::WorkingIntervals());
|
||||
}
|
||||
}
|
||||
auto result = ShiftedIntervals(withoutFullDays, delta);
|
||||
for (auto i = 0; i != 7; ++i) {
|
||||
if (full[i]) {
|
||||
result = ReplaceDayIntervals(
|
||||
result,
|
||||
i,
|
||||
Data::WorkingIntervals{ { { 0, kDay } } });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
const auto dayHoursText = [=](int day) {
|
||||
return rpl::combine(
|
||||
state->hours.value(),
|
||||
state->mineByDays.value(),
|
||||
state->myTimezone.value()
|
||||
) | rpl::map([=](
|
||||
const WorkingHours &hours,
|
||||
const WorkingIntervals &mine,
|
||||
bool my) {
|
||||
return FormatDayHours(hours, mine, my, day);
|
||||
});
|
||||
};
|
||||
const auto dayHoursTextValue = [=](rpl::producer<int> day) {
|
||||
return std::move(day)
|
||||
| rpl::map(dayHoursText)
|
||||
| rpl::flatten_latest();
|
||||
};
|
||||
|
||||
const auto openedWrap = inner->add(object_ptr<Ui::RpWidget>(inner));
|
||||
const auto opened = Ui::CreateChild<Ui::FlatLabel>(
|
||||
openedWrap,
|
||||
rpl::conditional(
|
||||
state->opened.value(),
|
||||
tr::lng_info_work_open(),
|
||||
tr::lng_info_work_closed()
|
||||
) | rpl::after_next(recount),
|
||||
st::infoHoursState);
|
||||
opened->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
const auto timing = Ui::CreateChild<Ui::FlatLabel>(
|
||||
openedWrap,
|
||||
OpensInText(
|
||||
state->opensIn.value(),
|
||||
dayHoursTextValue(state->day.value())
|
||||
) | rpl::after_next(recount),
|
||||
st::infoHoursValue);
|
||||
timing->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
state->opened.value() | rpl::start_with_next([=](bool value) {
|
||||
opened->setTextColorOverride(value
|
||||
? st::boxTextFgGood->c
|
||||
: st::boxTextFgError->c);
|
||||
}, opened->lifetime());
|
||||
|
||||
rpl::combine(
|
||||
openedWrap->widthValue(),
|
||||
opened->heightValue(),
|
||||
timing->sizeValue()
|
||||
) | rpl::start_with_next([=](int width, int h1, QSize size) {
|
||||
opened->moveToLeft(0, 0, width);
|
||||
timing->moveToRight(0, 0, width);
|
||||
|
||||
const auto margins = opened->getMargins();
|
||||
const auto added = margins.top() + margins.bottom();
|
||||
openedWrap->resize(width, std::max(h1, size.height()) - added);
|
||||
}, openedWrap->lifetime());
|
||||
|
||||
const auto labelWrap = inner->add(object_ptr<Ui::RpWidget>(inner));
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
labelWrap,
|
||||
tr::lng_info_hours_label(),
|
||||
st::infoLabel);
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
const auto link = Ui::CreateChild<Ui::LinkButton>(
|
||||
labelWrap,
|
||||
QString());
|
||||
rpl::combine(
|
||||
state->nonTrivial.value(),
|
||||
state->hours.value(),
|
||||
state->mine.value(),
|
||||
state->myTimezone.value()
|
||||
) | rpl::map([=](
|
||||
bool complex,
|
||||
const WorkingHours &hours,
|
||||
const WorkingIntervals &mine,
|
||||
bool my) {
|
||||
return (!complex || hours.intervals == mine)
|
||||
? rpl::single(QString())
|
||||
: my
|
||||
? tr::lng_info_hours_my_time()
|
||||
: tr::lng_info_hours_local_time();
|
||||
}) | rpl::flatten_latest(
|
||||
) | rpl::start_with_next([=](const QString &text) {
|
||||
link->setText(text);
|
||||
}, link->lifetime());
|
||||
link->setClickedCallback([=] {
|
||||
state->myTimezone = !state->myTimezone.current();
|
||||
});
|
||||
|
||||
rpl::combine(
|
||||
labelWrap->widthValue(),
|
||||
label->heightValue(),
|
||||
link->sizeValue()
|
||||
) | rpl::start_with_next([=](int width, int h1, QSize size) {
|
||||
label->moveToLeft(0, 0, width);
|
||||
link->moveToRight(0, 0, width);
|
||||
|
||||
const auto margins = label->getMargins();
|
||||
const auto added = margins.top() + margins.bottom();
|
||||
labelWrap->resize(width, std::max(h1, size.height()) - added);
|
||||
}, labelWrap->lifetime());
|
||||
|
||||
const auto other = inner->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
inner,
|
||||
object_ptr<Ui::VerticalLayout>(inner)));
|
||||
other->toggleOn(state->expanded.value(), anim::type::normal);
|
||||
other->finishAnimating();
|
||||
const auto days = other->entity();
|
||||
|
||||
for (auto i = 1; i != 7; ++i) {
|
||||
const auto dayWrap = days->add(
|
||||
object_ptr<Ui::RpWidget>(other),
|
||||
QMargins(0, st::infoHoursDaySkip, 0, 0));
|
||||
auto label = state->day.value() | rpl::map([=](int day) {
|
||||
switch ((day + i) % 7) {
|
||||
case 0: return tr::lng_hours_monday();
|
||||
case 1: return tr::lng_hours_tuesday();
|
||||
case 2: return tr::lng_hours_wednesday();
|
||||
case 3: return tr::lng_hours_thursday();
|
||||
case 4: return tr::lng_hours_friday();
|
||||
case 5: return tr::lng_hours_saturday();
|
||||
case 6: return tr::lng_hours_sunday();
|
||||
}
|
||||
Unexpected("Index in working hours.");
|
||||
}) | rpl::flatten_latest();
|
||||
const auto dayLabel = Ui::CreateChild<Ui::FlatLabel>(
|
||||
dayWrap,
|
||||
std::move(label),
|
||||
st::infoHoursDayLabel);
|
||||
dayLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
const auto dayHours = Ui::CreateChild<Ui::FlatLabel>(
|
||||
dayWrap,
|
||||
dayHoursTextValue(state->day.value()
|
||||
| rpl::map((rpl::mappers::_1 + i) % 7)),
|
||||
st::infoHoursValue);
|
||||
dayHours->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
rpl::combine(
|
||||
dayWrap->widthValue(),
|
||||
dayLabel->heightValue(),
|
||||
dayHours->sizeValue()
|
||||
) | rpl::start_with_next([=](int width, int h1, QSize size) {
|
||||
dayLabel->moveToLeft(0, 0, width);
|
||||
dayHours->moveToRight(0, 0, width);
|
||||
|
||||
const auto margins = dayLabel->getMargins();
|
||||
const auto added = margins.top() + margins.bottom();
|
||||
dayWrap->resize(width, std::max(h1, size.height()) - added);
|
||||
}, dayWrap->lifetime());
|
||||
}
|
||||
|
||||
button->setClickedCallback([=] {
|
||||
state->expanded = !state->expanded.current();
|
||||
});
|
||||
|
||||
result->toggleOn(state->hours.value(
|
||||
) | rpl::map([](const WorkingHours &data) {
|
||||
return bool(data);
|
||||
}));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Text, typename ToggleOn, typename Callback>
|
||||
auto AddActionButton(
|
||||
not_null<Ui::VerticalLayout*> parent,
|
||||
|
@ -563,6 +998,28 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
|
|||
}
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
tracker.track(result->add(CreateWorkingHours(result, user)));
|
||||
|
||||
auto locationText = user->session().changes().peerFlagsValue(
|
||||
user,
|
||||
Data::PeerUpdate::Flag::BusinessDetails
|
||||
) | rpl::map([=] {
|
||||
const auto &details = user->businessDetails();
|
||||
if (!details.location) {
|
||||
return TextWithEntities();
|
||||
} else if (!details.location.point) {
|
||||
return TextWithEntities{ details.location.address };
|
||||
}
|
||||
return Ui::Text::Link(
|
||||
TextUtilities::SingleLine(details.location.address),
|
||||
LocationClickHandler::Url(*details.location.point));
|
||||
});
|
||||
addInfoOneLine(
|
||||
tr::lng_info_location_label(),
|
||||
std::move(locationText),
|
||||
QString()
|
||||
).text->setLinksTrusted();
|
||||
}
|
||||
|
||||
AddMainButton(
|
||||
|
|
|
@ -70,27 +70,6 @@ private:
|
|||
return prefix + ' ' + data.name;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString FindClosestTimezoneId(
|
||||
const std::vector<Data::Timezone> &list) {
|
||||
const auto local = QDateTime::currentDateTime();
|
||||
const auto utc = QDateTime(local.date(), local.time(), Qt::UTC);
|
||||
const auto shift = base::unixtime::now() - (TimeId)::time(nullptr);
|
||||
const auto delta = int(utc.toSecsSinceEpoch())
|
||||
- int(local.toSecsSinceEpoch())
|
||||
- shift;
|
||||
const auto proj = [&](const Data::Timezone &value) {
|
||||
auto distance = value.utcOffset - delta;
|
||||
while (distance > 12 * 3600) {
|
||||
distance -= 24 * 3600;
|
||||
}
|
||||
while (distance < -12 * 3600) {
|
||||
distance += 24 * 3600;
|
||||
}
|
||||
return std::abs(distance);
|
||||
};
|
||||
return ranges::min_element(list, ranges::less(), proj)->id;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString FormatDayTime(
|
||||
TimeId time,
|
||||
bool showEndAsNextDay = false) {
|
||||
|
@ -372,7 +351,7 @@ void ChooseTimezoneBox(
|
|||
});
|
||||
|
||||
if (!ranges::contains(list, id, &Data::Timezone::id)) {
|
||||
id = FindClosestTimezoneId(list);
|
||||
id = Data::FindClosestTimezoneId(list);
|
||||
}
|
||||
const auto i = ranges::find(list, id, &Data::Timezone::id);
|
||||
const auto value = int(i - begin(list));
|
||||
|
@ -472,7 +451,7 @@ void AddWeekButton(
|
|||
}
|
||||
if (!intervals) {
|
||||
return tr::lng_hours_closed();
|
||||
} else if (intervals.list.front() == WorkingInterval{ 0, kDay }) {
|
||||
} else if (IsFullOpen(intervals)) {
|
||||
return tr::lng_hours_open_full();
|
||||
}
|
||||
return rpl::single(JoinIntervals(intervals));
|
||||
|
@ -613,7 +592,7 @@ void WorkingHours::setupContent(
|
|||
const auto now = _hours.current().timezoneId;
|
||||
if (!ranges::contains(value.list, now, &Data::Timezone::id)) {
|
||||
auto copy = _hours.current();
|
||||
copy.timezoneId = FindClosestTimezoneId(value.list);
|
||||
copy.timezoneId = Data::FindClosestTimezoneId(value.list);
|
||||
_hours = std::move(copy);
|
||||
}
|
||||
}, inner->lifetime());
|
||||
|
|
Loading…
Add table
Reference in a new issue