mirror of
https://github.com/rad4day/Waybar.git
synced 2023-12-21 10:22:59 +01:00
refactor(clock): remove struct waybar_time
The structure was used to pass the locale instance to the date formatter. All the supported versions of `fmt` are passing the locale parameter via `FormatContext.locale()` so we can remove the struct and simplify the code. While we at it, drop `date::make_zoned` in favor of CTAD on a `date::zoned_time` constructor.
This commit is contained in:
parent
ba498869c5
commit
67efe1af89
@ -5,11 +5,7 @@
|
|||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
#include "util/sleeper_thread.hpp"
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar::modules {
|
||||||
|
|
||||||
struct waybar_time;
|
|
||||||
|
|
||||||
namespace modules {
|
|
||||||
|
|
||||||
const std::string kCalendarPlaceholder = "calendar";
|
const std::string kCalendarPlaceholder = "calendar";
|
||||||
const std::string KTimezonedTimeListPlaceholder = "timezoned_time_list";
|
const std::string KTimezonedTimeListPlaceholder = "timezoned_time_list";
|
||||||
@ -36,12 +32,11 @@ class Clock : public ALabel {
|
|||||||
std::string fmt_str_weeks_;
|
std::string fmt_str_weeks_;
|
||||||
std::string fmt_str_calendar_;
|
std::string fmt_str_calendar_;
|
||||||
int fmt_weeks_left_pad_{0};
|
int fmt_weeks_left_pad_{0};
|
||||||
auto calendar_text(const waybar_time& wtime) -> std::string;
|
auto calendar_text(const date::zoned_seconds& ztime) -> std::string;
|
||||||
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
|
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
|
||||||
auto first_day_of_week() -> date::weekday;
|
auto first_day_of_week() -> date::weekday;
|
||||||
const date::time_zone* current_timezone();
|
const date::time_zone* current_timezone();
|
||||||
bool is_timezone_fixed();
|
bool is_timezone_fixed();
|
||||||
auto timezones_text(std::chrono::system_clock::time_point* now) -> std::string;
|
auto timezones_text(std::chrono::system_clock::time_point* now) -> std::string;
|
||||||
};
|
};
|
||||||
} // namespace modules
|
} // namespace waybar::modules
|
||||||
} // namespace waybar
|
|
||||||
|
@ -3,17 +3,8 @@
|
|||||||
#include <date/tz.h>
|
#include <date/tz.h>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
namespace waybar {
|
template <typename Duration, typename TimeZonePtr>
|
||||||
|
struct fmt::formatter<date::zoned_time<Duration, TimeZonePtr>> {
|
||||||
struct waybar_time {
|
|
||||||
std::locale locale;
|
|
||||||
date::zoned_seconds ztime;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace waybar
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<waybar::waybar_time> {
|
|
||||||
std::string_view specs;
|
std::string_view specs;
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
@ -33,7 +24,11 @@ struct fmt::formatter<waybar::waybar_time> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const waybar::waybar_time& t, FormatContext& ctx) {
|
auto format(const date::zoned_time<Duration, TimeZonePtr>& ztime, FormatContext& ctx) {
|
||||||
return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(specs), t.ztime));
|
if (ctx.locale()) {
|
||||||
|
const auto loc = ctx.locale().template get<std::locale>();
|
||||||
|
return fmt::format_to(ctx.out(), "{}", date::format(loc, fmt::to_string(specs), ztime));
|
||||||
|
}
|
||||||
|
return fmt::format_to(ctx.out(), "{}", date::format(fmt::to_string(specs), ztime));
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -9,15 +9,13 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "util/date.hpp"
|
||||||
#include "util/ustring_clen.hpp"
|
#include "util/ustring_clen.hpp"
|
||||||
#include "util/waybar_time.hpp"
|
|
||||||
#ifdef HAVE_LANGINFO_1STDAY
|
#ifdef HAVE_LANGINFO_1STDAY
|
||||||
#include <langinfo.h>
|
#include <langinfo.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using waybar::waybar_time;
|
|
||||||
|
|
||||||
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||||
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true),
|
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true),
|
||||||
current_time_zone_idx_(0),
|
current_time_zone_idx_(0),
|
||||||
@ -110,15 +108,13 @@ bool waybar::modules::Clock::is_timezone_fixed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Clock::update() -> void {
|
auto waybar::modules::Clock::update() -> void {
|
||||||
auto time_zone = current_timezone();
|
const auto* time_zone = current_timezone();
|
||||||
auto now = std::chrono::system_clock::now();
|
auto now = std::chrono::system_clock::now();
|
||||||
waybar_time wtime = {locale_,
|
auto ztime = date::zoned_time{time_zone, date::floor<std::chrono::seconds>(now)};
|
||||||
date::make_zoned(time_zone, date::floor<std::chrono::seconds>(now))};
|
|
||||||
|
|
||||||
auto shifted_date = date::year_month_day{date::floor<date::days>(now)} + calendar_shift_;
|
auto shifted_date = date::year_month_day{date::floor<date::days>(now)} + calendar_shift_;
|
||||||
auto now_shifted = date::sys_days{shifted_date} + (now - date::floor<date::days>(now));
|
auto now_shifted = date::sys_days{shifted_date} + (now - date::floor<date::days>(now));
|
||||||
waybar_time shifted_wtime = {
|
auto shifted_ztime = date::zoned_time{time_zone, date::floor<std::chrono::seconds>(now_shifted)};
|
||||||
locale_, date::make_zoned(time_zone, date::floor<std::chrono::seconds>(now_shifted))};
|
|
||||||
|
|
||||||
std::string text = "";
|
std::string text = "";
|
||||||
if (!is_timezone_fixed()) {
|
if (!is_timezone_fixed()) {
|
||||||
@ -127,7 +123,7 @@ auto waybar::modules::Clock::update() -> void {
|
|||||||
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
|
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
|
||||||
text = fmt::format(locale_, format_, localtime);
|
text = fmt::format(locale_, format_, localtime);
|
||||||
} else {
|
} else {
|
||||||
text = fmt::format(format_, wtime);
|
text = fmt::format(locale_, format_, ztime);
|
||||||
}
|
}
|
||||||
label_.set_markup(text);
|
label_.set_markup(text);
|
||||||
|
|
||||||
@ -136,13 +132,13 @@ auto waybar::modules::Clock::update() -> void {
|
|||||||
std::string calendar_lines{""};
|
std::string calendar_lines{""};
|
||||||
std::string timezoned_time_lines{""};
|
std::string timezoned_time_lines{""};
|
||||||
if (is_calendar_in_tooltip_) {
|
if (is_calendar_in_tooltip_) {
|
||||||
calendar_lines = calendar_text(shifted_wtime);
|
calendar_lines = calendar_text(shifted_ztime);
|
||||||
}
|
}
|
||||||
if (is_timezoned_list_in_tooltip_) {
|
if (is_timezoned_list_in_tooltip_) {
|
||||||
timezoned_time_lines = timezones_text(&now);
|
timezoned_time_lines = timezones_text(&now);
|
||||||
}
|
}
|
||||||
auto tooltip_format = config_["tooltip-format"].asString();
|
auto tooltip_format = config_["tooltip-format"].asString();
|
||||||
text = fmt::format(tooltip_format, shifted_wtime,
|
text = fmt::format(locale_, tooltip_format, shifted_ztime,
|
||||||
fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines),
|
fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines),
|
||||||
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
|
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
|
||||||
label_.set_tooltip_markup(text);
|
label_.set_tooltip_markup(text);
|
||||||
@ -190,8 +186,8 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string {
|
auto waybar::modules::Clock::calendar_text(const date::zoned_seconds& ztime) -> std::string {
|
||||||
const auto daypoint = date::floor<date::days>(wtime.ztime.get_local_time());
|
const auto daypoint = date::floor<date::days>(ztime.get_local_time());
|
||||||
const auto ymd{date::year_month_day{daypoint}};
|
const auto ymd{date::year_month_day{daypoint}};
|
||||||
|
|
||||||
if (calendar_cached_ymd_ == ymd) {
|
if (calendar_cached_ymd_ == ymd) {
|
||||||
@ -318,7 +314,6 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
std::stringstream os;
|
std::stringstream os;
|
||||||
waybar_time wtime;
|
|
||||||
for (size_t time_zone_idx = 0; time_zone_idx < time_zones_.size(); ++time_zone_idx) {
|
for (size_t time_zone_idx = 0; time_zone_idx < time_zones_.size(); ++time_zone_idx) {
|
||||||
if (static_cast<int>(time_zone_idx) == current_time_zone_idx_) {
|
if (static_cast<int>(time_zone_idx) == current_time_zone_idx_) {
|
||||||
continue;
|
continue;
|
||||||
@ -327,8 +322,8 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin
|
|||||||
if (!timezone) {
|
if (!timezone) {
|
||||||
timezone = date::current_zone();
|
timezone = date::current_zone();
|
||||||
}
|
}
|
||||||
wtime = {locale_, date::make_zoned(timezone, date::floor<std::chrono::seconds>(*now))};
|
auto ztime = date::zoned_time{timezone, date::floor<std::chrono::seconds>(*now)};
|
||||||
os << fmt::format(format_, wtime) << '\n';
|
os << fmt::format(locale_, format_, ztime) << '\n';
|
||||||
}
|
}
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
94
test/date.cpp
Normal file
94
test/date.cpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#include "util/date.hpp"
|
||||||
|
|
||||||
|
#if __has_include(<catch2/catch_test_macros.hpp>)
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <catch2/matchers/catch_matchers_all.hpp>
|
||||||
|
#else
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#endif
|
||||||
|
#include <chrono>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
using namespace std::literals::chrono_literals;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that the date/time formatter with locale and timezone support is working as expected.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const date::zoned_time<std::chrono::seconds> TEST_TIME = date::zoned_time{
|
||||||
|
"UTC", date::local_days{date::Monday[1] / date::January / 2022} + 13h + 4min + 5s};
|
||||||
|
|
||||||
|
TEST_CASE("Format UTC time", "[clock][util]") {
|
||||||
|
const auto loc = std::locale("C");
|
||||||
|
const auto tm = TEST_TIME;
|
||||||
|
|
||||||
|
REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified
|
||||||
|
REQUIRE(fmt::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC");
|
||||||
|
REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
||||||
|
|
||||||
|
/* Test a few locales that are most likely to be present */
|
||||||
|
SECTION("US locale") {
|
||||||
|
try {
|
||||||
|
const auto loc = std::locale("en_US");
|
||||||
|
|
||||||
|
REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified
|
||||||
|
REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704
|
||||||
|
Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM"));
|
||||||
|
REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "01/03/2022 01:04:05 PM");
|
||||||
|
REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
||||||
|
} catch (const std::runtime_error&) {
|
||||||
|
// locale not found; ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SECTION("GB locale") {
|
||||||
|
try {
|
||||||
|
const auto loc = std::locale("en_GB");
|
||||||
|
|
||||||
|
REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified
|
||||||
|
REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704
|
||||||
|
Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05"));
|
||||||
|
REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "03/01/22 13:04:05");
|
||||||
|
REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
||||||
|
} catch (const std::runtime_error&) {
|
||||||
|
// locale not found; ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Format zoned time", "[clock][util]") {
|
||||||
|
const auto loc = std::locale("C");
|
||||||
|
const auto tm = date::zoned_time{"America/New_York", TEST_TIME};
|
||||||
|
|
||||||
|
REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified
|
||||||
|
REQUIRE(fmt::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST");
|
||||||
|
REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
||||||
|
|
||||||
|
/* Test a few locales that are most likely to be present */
|
||||||
|
SECTION("US locale") {
|
||||||
|
try {
|
||||||
|
const auto loc = std::locale("en_US");
|
||||||
|
|
||||||
|
REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified
|
||||||
|
REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704
|
||||||
|
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM"));
|
||||||
|
REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "01/03/2022 08:04:05 AM");
|
||||||
|
REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
||||||
|
} catch (const std::runtime_error&) {
|
||||||
|
// locale not found; ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("GB locale") {
|
||||||
|
try {
|
||||||
|
const auto loc = std::locale("en_GB");
|
||||||
|
|
||||||
|
REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified
|
||||||
|
REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704
|
||||||
|
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05"));
|
||||||
|
REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "03/01/22 08:04:05");
|
||||||
|
REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
||||||
|
} catch (const std::runtime_error&) {
|
||||||
|
// locale not found; ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@ test_src = files(
|
|||||||
|
|
||||||
if tz_dep.found()
|
if tz_dep.found()
|
||||||
test_dep += tz_dep
|
test_dep += tz_dep
|
||||||
test_src += files('waybar_time.cpp')
|
test_src += files('date.cpp')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
waybar_test = executable(
|
waybar_test = executable(
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
#include "util/waybar_time.hpp"
|
|
||||||
|
|
||||||
#include <date/date.h>
|
|
||||||
#include <date/tz.h>
|
|
||||||
|
|
||||||
#if __has_include(<catch2/catch_test_macros.hpp>)
|
|
||||||
#include <catch2/catch_test_macros.hpp>
|
|
||||||
#include <catch2/matchers/catch_matchers_all.hpp>
|
|
||||||
#else
|
|
||||||
#include <catch2/catch.hpp>
|
|
||||||
#endif
|
|
||||||
#include <chrono>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
using namespace std::literals::chrono_literals;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check that the date/time formatter with locale and timezone support is working as expected.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const date::zoned_time<std::chrono::seconds> TEST_TIME = date::make_zoned(
|
|
||||||
"UTC", date::local_days{date::Monday[1] / date::January / 2022} + 13h + 4min + 5s);
|
|
||||||
|
|
||||||
TEST_CASE("Format UTC time", "[clock][util]") {
|
|
||||||
waybar::waybar_time tm{std::locale("C"), TEST_TIME};
|
|
||||||
|
|
||||||
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
|
|
||||||
REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC");
|
|
||||||
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
|
||||||
|
|
||||||
/* Test a few locales that are most likely to be present */
|
|
||||||
SECTION("US locale") {
|
|
||||||
try {
|
|
||||||
tm.locale = std::locale("en_US");
|
|
||||||
|
|
||||||
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
|
|
||||||
REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
|
|
||||||
Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM"));
|
|
||||||
REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 01:04:05 PM");
|
|
||||||
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
|
||||||
} catch (const std::runtime_error&) {
|
|
||||||
// locale not found; ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SECTION("GB locale") {
|
|
||||||
try {
|
|
||||||
tm.locale = std::locale("en_GB");
|
|
||||||
|
|
||||||
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
|
|
||||||
REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
|
|
||||||
Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05"));
|
|
||||||
REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 13:04:05");
|
|
||||||
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
|
||||||
} catch (const std::runtime_error&) {
|
|
||||||
// locale not found; ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Format zoned time", "[clock][util]") {
|
|
||||||
waybar::waybar_time tm{std::locale("C"), date::make_zoned("America/New_York", TEST_TIME)};
|
|
||||||
|
|
||||||
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
|
|
||||||
REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST");
|
|
||||||
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
|
||||||
|
|
||||||
/* Test a few locales that are most likely to be present */
|
|
||||||
SECTION("US locale") {
|
|
||||||
try {
|
|
||||||
tm.locale = std::locale("en_US");
|
|
||||||
|
|
||||||
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
|
|
||||||
REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
|
|
||||||
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM"));
|
|
||||||
REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 08:04:05 AM");
|
|
||||||
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
|
||||||
} catch (const std::runtime_error&) {
|
|
||||||
// locale not found; ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("GB locale") {
|
|
||||||
try {
|
|
||||||
tm.locale = std::locale("en_GB");
|
|
||||||
|
|
||||||
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
|
|
||||||
REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
|
|
||||||
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05"));
|
|
||||||
REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 08:04:05");
|
|
||||||
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
|
||||||
} catch (const std::runtime_error&) {
|
|
||||||
// locale not found; ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user