Address macOS issue where right-clicking a username in the main chat (#4523)

* Address macOS issue where right-clicking a username in the main chat (or game chat) areas would pop up a seemingly empty user profile. This is because the resize event is overridden and doesn't actually attempt to resize based on the size hint of the dialog. Now that we're explicit with the call, this resize should be forced and have comparable results to popping up user profile from the user list.

* use datetime for calculating account age (#4526)

* use datetime for calculating account age

make translating easier by using tr multiples
automatically account for leap days

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
This commit is contained in:
Zach H 2022-01-16 16:51:13 -05:00 committed by GitHub
parent baaf22d0c4
commit d61c604bf4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 30 deletions

View file

@ -11,20 +11,15 @@
#include <QDateTime> #include <QDateTime>
#include <QGridLayout> #include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel> #include <QLabel>
#include <QMessageBox> #include <QMessageBox>
const qint64 SIXTY = 60;
const qint64 HOURS_IN_A_DAY = 24;
const qint64 DAYS_IN_A_YEAR = 365;
UserInfoBox::UserInfoBox(AbstractClient *_client, bool _editable, QWidget *parent, Qt::WindowFlags flags) UserInfoBox::UserInfoBox(AbstractClient *_client, bool _editable, QWidget *parent, Qt::WindowFlags flags)
: QWidget(parent, flags), client(_client), editable(_editable) : QWidget(parent, flags), client(_client), editable(_editable)
{ {
QFont nameFont = nameLabel.font(); QFont nameFont = nameLabel.font();
nameFont.setBold(true); nameFont.setBold(true);
nameFont.setPointSize(nameFont.pointSize() * 1.5); nameFont.setPointSizeF(nameFont.pointSizeF() * 1.5);
nameLabel.setFont(nameFont); nameLabel.setFont(nameFont);
avatarLabel.setMinimumSize(200, 200); avatarLabel.setMinimumSize(200, 200);
@ -90,7 +85,6 @@ void UserInfoBox::updateInfo(const ServerInfo_User &user)
avatarPixmap = avatarPixmap =
UserLevelPixmapGenerator::generatePixmap(64, userLevel, false, QString::fromStdString(user.privlevel())); UserLevelPixmapGenerator::generatePixmap(64, userLevel, false, QString::fromStdString(user.privlevel()));
} }
avatarLabel.setPixmap(avatarPixmap.scaled(400, 200, Qt::KeepAspectRatio, Qt::SmoothTransformation));
nameLabel.setText(QString::fromStdString(user.name())); nameLabel.setText(QString::fromStdString(user.name()));
realNameLabel2.setText(QString::fromStdString(user.real_name())); realNameLabel2.setText(QString::fromStdString(user.real_name()));
@ -129,32 +123,37 @@ void UserInfoBox::updateInfo(const ServerInfo_User &user)
QString accountAgeString = tr("Unregistered user"); QString accountAgeString = tr("Unregistered user");
if (userLevel.testFlag(ServerInfo_User::IsAdmin) || userLevel.testFlag(ServerInfo_User::IsModerator) || if (userLevel.testFlag(ServerInfo_User::IsAdmin) || userLevel.testFlag(ServerInfo_User::IsModerator) ||
userLevel.testFlag(ServerInfo_User::IsRegistered)) { userLevel.testFlag(ServerInfo_User::IsRegistered)) {
if (user.accountage_secs() == 0) accountAgeString = getAgeString(user.accountage_secs());
accountAgeString = tr("Unknown");
else {
qint64 seconds = user.accountage_secs();
qint64 minutes = seconds / SIXTY;
qint64 hours = minutes / SIXTY;
qint64 days = hours / HOURS_IN_A_DAY;
qint64 years = days / DAYS_IN_A_YEAR;
qint64 daysMinusYears = days - (years * DAYS_IN_A_YEAR);
accountAgeString = "";
if (years >= 1) {
accountAgeString = QString::number(years);
accountAgeString.append(" ");
accountAgeString.append(years == 1 ? tr("Year") : tr("Years"));
accountAgeString.append(" ");
}
accountAgeString.append(QString::number(daysMinusYears));
accountAgeString.append(" ");
accountAgeString.append(days == 1 ? tr("Day") : tr("Days"));
}
} }
accountAgeLabel2.setText(accountAgeString); accountAgeLabel2.setText(accountAgeString);
} }
QString UserInfoBox::getAgeString(int ageSeconds)
{
QString accountAgeString = tr("Unknown");
if (ageSeconds == 0)
return accountAgeString;
auto date = QDateTime::fromTime_t(QDateTime::currentSecsSinceEpoch() - ageSeconds).date();
if (!date.isValid())
return accountAgeString;
QString dateString = QLocale().toString(date, QLocale::ShortFormat);
auto now = QDate::currentDate();
auto daysAndYears = getDaysAndYearsBetween(date, now);
QString yearString;
if (daysAndYears.second > 0) {
yearString = tr("%n Year(s), ", "amount of years (only shown if more than 0)", daysAndYears.second);
}
accountAgeString =
tr("%10%n Day(s) %20", "amount of years (if more than 0), amount of days, date in local short format",
daysAndYears.first)
.arg(yearString)
.arg(dateString);
return accountAgeString;
}
void UserInfoBox::updateInfo(const QString &userName) void UserInfoBox::updateInfo(const QString &userName)
{ {
Command_GetUserInfo cmd; Command_GetUserInfo cmd;
@ -171,6 +170,7 @@ void UserInfoBox::processResponse(const Response &r)
{ {
const Response_GetUserInfo &response = r.GetExtension(Response_GetUserInfo::ext); const Response_GetUserInfo &response = r.GetExtension(Response_GetUserInfo::ext);
updateInfo(response.user_info()); updateInfo(response.user_info());
resize(sizeHint());
show(); show();
} }
@ -257,7 +257,7 @@ void UserInfoBox::processEditResponse(const Response &r)
case Response::RespInternalError: case Response::RespInternalError:
default: default:
QMessageBox::critical(this, tr("Error"), QMessageBox::critical(this, tr("Error"),
tr("An error occured while trying to update your user informations.")); tr("An error occurred while trying to update your user information."));
break; break;
} }
} }

View file

@ -1,6 +1,7 @@
#ifndef USERINFOBOX_H #ifndef USERINFOBOX_H
#define USERINFOBOX_H #define USERINFOBOX_H
#include <QDateTime>
#include <QLabel> #include <QLabel>
#include <QPushButton> #include <QPushButton>
#include <QWidget> #include <QWidget>
@ -20,9 +21,18 @@ private:
QPushButton editButton, passwordButton, avatarButton; QPushButton editButton, passwordButton, avatarButton;
QPixmap avatarPixmap; QPixmap avatarPixmap;
static QString getAgeString(int ageSeconds);
public: public:
UserInfoBox(AbstractClient *_client, bool editable, QWidget *parent = nullptr, Qt::WindowFlags flags = {}); UserInfoBox(AbstractClient *_client, bool editable, QWidget *parent = nullptr, Qt::WindowFlags flags = {});
void retranslateUi(); void retranslateUi();
inline static QPair<int, int> getDaysAndYearsBetween(const QDate &then, const QDate &now)
{
int years = now.addDays(1 - then.dayOfYear()).year() - then.year(); // there is no yearsTo
int days = then.addYears(years).daysTo(now);
return {days, years};
}
private slots: private slots:
void processResponse(const Response &r); void processResponse(const Response &r);
void processEditResponse(const Response &r); void processEditResponse(const Response &r);

View file

@ -1,12 +1,15 @@
enable_testing() enable_testing()
add_test(NAME dummy_test COMMAND dummy_test) add_test(NAME dummy_test COMMAND dummy_test)
add_test(NAME expression_test COMMAND expression_test) add_test(NAME expression_test COMMAND expression_test)
add_test(NAME test_age_formatting COMMAND test_age_formatting)
add_test(NAME password_hash_test COMMAND password_hash_test) add_test(NAME password_hash_test COMMAND password_hash_test)
# Find GTest # Find GTest
add_executable(dummy_test dummy_test.cpp) add_executable(dummy_test dummy_test.cpp)
add_executable(expression_test expression_test.cpp) add_executable(expression_test expression_test.cpp)
add_executable(test_age_formatting test_age_formatting.cpp)
add_executable(password_hash_test password_hash_test.cpp) add_executable(password_hash_test password_hash_test.cpp)
find_package(GTest) find_package(GTest)
@ -37,6 +40,7 @@ if(NOT GTEST_FOUND)
SET(GTEST_BOTH_LIBRARIES gtest) SET(GTEST_BOTH_LIBRARIES gtest)
add_dependencies(dummy_test gtest) add_dependencies(dummy_test gtest)
add_dependencies(expression_test gtest) add_dependencies(expression_test gtest)
add_dependencies(test_age_formatting gtest)
add_dependencies(password_hash_test gtest) add_dependencies(password_hash_test gtest)
endif() endif()
@ -47,6 +51,7 @@ set(TEST_QT_MODULES Qt5::Widgets)
include_directories(${GTEST_INCLUDE_DIRS}) include_directories(${GTEST_INCLUDE_DIRS})
target_link_libraries(dummy_test Threads::Threads ${GTEST_BOTH_LIBRARIES}) target_link_libraries(dummy_test Threads::Threads ${GTEST_BOTH_LIBRARIES})
target_link_libraries(expression_test cockatrice_common Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}) target_link_libraries(expression_test cockatrice_common Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES})
target_link_libraries(test_age_formatting Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES})
target_link_libraries(password_hash_test cockatrice_common Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}) target_link_libraries(password_hash_test cockatrice_common Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES})
add_subdirectory(carddatabase) add_subdirectory(carddatabase)

View file

@ -0,0 +1,44 @@
#include "../cockatrice/src/userinfobox.h"
#include "gtest/gtest.h"
namespace
{
using dayyear = QPair<int, int>;
TEST(AgeFormatting, Zero)
{
auto got = UserInfoBox::getDaysAndYearsBetween(QDate(2000, 1, 1), QDate(2000, 1, 1));
ASSERT_EQ(got, dayyear(0, 0)) << "these are the same day";
}
TEST(AgeFormatting, LeapDay)
{
auto got = UserInfoBox::getDaysAndYearsBetween(QDate(2000, 2, 28), QDate(2000, 3, 1));
ASSERT_EQ(got, dayyear(2, 0)) << "there is a leap day in between these days";
}
TEST(AgeFormatting, LeapYear)
{
auto got = UserInfoBox::getDaysAndYearsBetween(QDate(2000, 1, 1), QDate(2001, 1, 1));
ASSERT_EQ(got, dayyear(0, 1)) << "there is a leap day in between these dates, but that's fine";
}
TEST(AgeFormatting, LeapDayWithYear)
{
auto got = UserInfoBox::getDaysAndYearsBetween(QDate(2000, 2, 28), QDate(2001, 3, 1));
ASSERT_EQ(got, dayyear(1, 1)) << "there is a leap day in between these days but not in the last year";
}
TEST(AgeFormatting, LeapDayThisYear)
{
auto got = UserInfoBox::getDaysAndYearsBetween(QDate(2003, 2, 28), QDate(2004, 3, 1));
ASSERT_EQ(got, dayyear(2, 1)) << "there is a leap day in between these days this year";
}
} // namespace
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}