Search added, but change window and back lets app crash

This commit is contained in:
Torsten Schulz
2024-01-28 13:43:36 +01:00
parent ffd01194da
commit c9bffa64cd
6 changed files with 381 additions and 17 deletions

View File

@@ -33,6 +33,9 @@
#include <Wt/WFileUpload.h>
#include <Wt/Utils.h>
#include <Wt/WSound.h>
#include <Wt/WSlider.h>
#include <Wt/WGridLayout.h>
#include <Wt/WCheckBox.h>
App::App(const Wt::WEnvironment &env, Broadcast &server):
Wt::WApplication(env),
@@ -50,8 +53,10 @@ App::App(const Wt::WEnvironment &env, Broadcast &server):
createUserListContainer(horizontalContainer);
createContentContainer(horizontalContainer);
createImprintContainer(verticalContainer);
reSetUser();
if (userName == "") {
showLogin();
Wt::WMessageBox::show("Cookie information", "By technical reasons we set a cookie. This cookie isn't tracking anything or will be saved in a database.", Wt::StandardButton::Ok);
} else {
startChat();
}
@@ -68,6 +73,20 @@ void App::initApp() {
useStyleSheet("style.css");
}
void App::reSetUser() {
if (env_.cookies().contains("wtd")) {
auto userData = server_.reSetUser(env_.cookies().find("wtd")->second, sessionId());
Wt::Json::Object emptyObject = {};
if (userData != emptyObject) {
userName = (std::string)userData["username"];
gender = (std::string)userData["gender"];
country = (std::string)userData["country"];
isoCountryCode = (std::string)userData["iso-country-code"];
age = (int)userData["age"];
}
}
}
Wt::WVBoxLayout *App::createVerticalLayout() {
auto verticalBox = root()->addNew<Wt::WContainerWidget>();
verticalBox->setHeight(Wt::WLength(100, Wt::LengthUnit::Percentage));
@@ -177,6 +196,7 @@ void App::handleLogin(Wt::WLineEdit* userName, Wt::WComboBox* countryWidget, Wt:
setUserData(nick, countryWidget, ageWidget, genderWidget);
connectToServer();
startChat();
setCookie("wtd", sessionId(), 21000);
} catch (const std::exception& e) {
Wt::WMessageBox::show("Attention", e.what(), Wt::StandardButton::Ok);
}
@@ -243,7 +263,7 @@ void App::populateGenderComboBox(Wt::WComboBox *genderWidget) {
std::string App::getGenderShortByGender(std::string gender) {
for (const auto &genderItem: genders_) {
if (gender == genderItem.second) {
return genderItem.first;
return genderItem.first.toUTF8();
}
}
return "";
@@ -258,15 +278,7 @@ void App::updateUserlist(Wt::Json::Array unsortedUserList) {
userListContainer_->setOverflow(Wt::Overflow::Auto);
auto sortedUserList = sortUserList(unsortedUserList);
for (Wt::Json::Object &user: sortedUserList) {
auto userName = (std::string)user["name"];
auto userItem = layout->addNew<Wt::WText>(Wt::WString("{1} ({2})").arg(userName).arg((int)user["age"]));
userItem->setStyleClass(Wt::WString("userlist-item userlist-gender-{1}").arg((std::string)user["gender"]));
userItem->setHeight(Wt::WLength(2, Wt::LengthUnit::FontEm));
userItem->setPadding(Wt::WLength(3, Wt::LengthUnit::Pixel));
userItem->clicked().connect([=, this]() {
requestConversation(userName);
});
Wt::WCssDecorationStyle style;
addUserItemToLayout(layout, user);
}
layout->addWidget(std::make_unique<Wt::WText>(), 1)->setStyleClass("height-spacer userlist");
triggerUpdate();
@@ -768,7 +780,7 @@ void App::addIdentifier() {
void App::addSearchButton() {
auto searchButton = menuContainer_->addNew<Wt::WPushButton>("Search");
searchButton->clicked().connect(this, &App::executeSearch);
searchButton->clicked().connect(this, &App::showSearchWindow);
}
void App::addInboxButton() {
@@ -776,11 +788,203 @@ void App::addInboxButton() {
inbox_->clicked().connect(this, &App::openInbox);
}
void App::executeSearch() {
void App::showSearchWindow() {
auto contentLayout = resetSearchFields();
auto userNameField = setupNameSearchField(contentLayout);
auto ageSearchFields = setupSearchFields(contentLayout);
auto countryFields = setupCountryDropDown(contentLayout);
auto gendersFields = setupGendersDropDown(contentLayout);
auto searchResultContainer = setupSearchButton(contentLayout);
restoreSearchFields(searchResultContainer, userNameField, ageSearchFields.first, ageSearchFields.second,
countryFields.second, gendersFields.second, countryFields.first, gendersFields.first);
}
Wt::WVBoxLayout *App::resetSearchFields() {
currentConversationWith_ = "";
contentContainer_->clear();
contentContainer_->addNew<Wt::WText>("search");
inboxOpen_ = false;
auto contentLayout = contentContainer_->setLayout(std::make_unique<Wt::WVBoxLayout>());
contentLayout->addNew<Wt::WText>("<h2>Search</h2>");
return contentLayout;
}
std::pair<Wt::WSpinBox*, Wt::WSpinBox*> App::setupSearchFields(Wt::WVBoxLayout *contentLayout) {
auto minAgeEdit = addSearchItemLine<Wt::WSpinBox>(contentLayout, "From age");
minAgeEdit->setRange(18, 150);
minAgeEdit->setValue(18);
minAgeEdit->changed().connect([=, this] { searchFields.minAge = minAgeEdit->value(); });
auto maxAgeEdit = addSearchItemLine<Wt::WSpinBox>(contentLayout, "To age");
maxAgeEdit->setRange(18, 150);
maxAgeEdit->setValue(150);
maxAgeEdit->changed().connect([=, this] { searchFields.maxAge = maxAgeEdit->value(); });
return {minAgeEdit, maxAgeEdit};
}
std::pair<Wt::WPushButton*, Wt::WContainerWidget*> App::setupCountryDropDown(Wt::WVBoxLayout *contentLayout) {
auto countryDropDown = std::make_unique<Wt::WContainerWidget>();
countryDropDown->setInline(true);
countryDropDown->setStyleClass("countries-drop-down");
auto countryDropDownContainer = countryDropDown.get();
auto countryOpenList = addSearchItemLine<Wt::WPushButton>(contentLayout, "Country", std::move(countryDropDown));
countryDropDownContainer->hide();
countryOpenList->clicked().connect([=]() { if (countryDropDownContainer->isHidden()) { countryDropDownContainer->show();} else { countryDropDownContainer->hide(); } });
countryOpenList->setText("All");
std::map<Wt::WString, Wt::WString> countries = server_.countries();
addItem("All", countryDropDownContainer, countryOpenList, &searchFields.countries, true);
addItem(country, countryDropDownContainer, countryOpenList, &searchFields.countries);
for (const auto &itemCountry: countries) {
if (itemCountry.first.toUTF8() != country) {
addItem(itemCountry.first.toUTF8(), countryDropDownContainer, countryOpenList, &searchFields.countries);
}
}
return {countryOpenList, countryDropDownContainer};
}
std::pair<Wt::WPushButton*, Wt::WContainerWidget*> App::setupGendersDropDown(Wt::WVBoxLayout *contentLayout) {
auto gendersDropDown = std::make_unique<Wt::WContainerWidget>();
gendersDropDown->setInline(true);
gendersDropDown->setStyleClass("countries-drop-down");
auto gendersDropDownContainer = gendersDropDown.get();
auto gendersOpenList = addSearchItemLine<Wt::WPushButton>(contentLayout, "Genders", std::move(gendersDropDown));
gendersDropDownContainer->hide();
gendersOpenList->clicked().connect([=]() { if (gendersDropDownContainer->isHidden()) { gendersDropDownContainer->show();} else { gendersDropDownContainer->hide(); } });
gendersOpenList->setText("All");
addItem("All", gendersDropDownContainer, gendersOpenList, &searchFields.gender, true);
std::map<Wt::WString, Wt::WString> swappedGenders;
for (const auto& pair : genders_) {
swappedGenders[pair.second] = pair.first;
}
for (const auto &itemGender: swappedGenders) {
addItem(itemGender.first.toUTF8(), gendersDropDownContainer, gendersOpenList, &searchFields.gender);
}
return {gendersOpenList, gendersDropDownContainer};
}
void App::addItem(const std::string& country, Wt::WContainerWidget *dropDownContainer, Wt::WPushButton *openListButton, std::unordered_set<std::string> *saveItems, bool isSelected) {
auto menuItem = dropDownContainer->addNew<Wt::WCheckBox>(country);
menuItem->changed().connect([=, this]() mutable { itemChanged(menuItem, dropDownContainer, openListButton, saveItems); });
menuItem->setInline(false);
if (isSelected) {
menuItem->setChecked();
}
}
void App::addUserItemToLayout(Wt::WVBoxLayout *layout, Wt::Json::Object userObject) {
auto userName = (std::string)userObject["name"];
auto userItem = layout->addNew<Wt::WText>(Wt::WString("{1} ({2})").arg(userName).arg((int)userObject["age"]));
userItem->setStyleClass(Wt::WString("userlist-item userlist-gender-{1}").arg((std::string)userObject["gender"]));
userItem->setHeight(Wt::WLength(2, Wt::LengthUnit::FontEm));
userItem->setPadding(Wt::WLength(3, Wt::LengthUnit::Pixel));
userItem->clicked().connect([=, this]() {
requestConversation(userName);
});
}
std::unordered_set<std::string> App::gendersListToShortGendersList(std::unordered_set<std::string> gendersList) {
std::unordered_set<std::string> result;
for (const auto &gender: gendersList) {
result.insert(genderShortOfGender(gender));
}
return result;
}
std::string App::genderShortOfGender(const std::string incomingGender) {
for (const auto &genderItem: genders_) {
if (incomingGender == genderItem.second) {
return genderItem.first.toUTF8();
}
}
return incomingGender;
}
void App::itemChanged(Wt::WCheckBox *item, Wt::WContainerWidget *dropDownContainer, Wt::WPushButton *openButton, std::unordered_set<std::string> *saveItems) {
saveItems->clear();
bool unselect = (item->text() == "All" && item->isChecked());
for (auto &widgetItem: dropDownContainer->children()) {
auto widgetCheckBox = static_cast<Wt::WCheckBox*>(widgetItem);
if (unselect && widgetCheckBox->text() != "All" && widgetCheckBox->isChecked()) {
widgetCheckBox->setChecked(false);
}
if (widgetCheckBox->isChecked()) {
saveItems->insert(widgetCheckBox->text().toUTF8());
}
}
if (saveItems->size() > 1 && saveItems->find("All") != saveItems->end()) {
saveItems->erase(saveItems->find("All"));
static_cast<Wt::WCheckBox*>(dropDownContainer->children().at(0))->setChecked(false);
}
if (saveItems->empty()) {
saveItems->insert("All");
}
std::string result;
for (const auto &selected: *saveItems) {
result += (result.empty() ? "" : ", ") + selected;
}
openButton->setText(result);
}
void App::restoreSearchFields(Wt::WContainerWidget *searchResultContainer, Wt::WLineEdit *userNameEdit, Wt::WSpinBox *minAgeEdit,
Wt::WSpinBox *maxAgeEdit, Wt::WContainerWidget *countryDropDownContainer, Wt::WContainerWidget *gendersDropDownContainer,
Wt::WPushButton *countryOpenList, Wt::WPushButton *gendersOpenList) {
if (!searchFields.set) {
searchFields = Search(searchResultContainer);
} else {
userNameEdit->setValueText(searchFields.userName);
minAgeEdit->setValue(searchFields.minAge);
maxAgeEdit->setValue(searchFields.maxAge);
for (auto countryWidget: countryDropDownContainer->children()) {
auto countryCheckBox = (Wt::WCheckBox*)countryWidget;
countryCheckBox->setChecked(searchFields.countries.find(countryCheckBox->text().toUTF8()) != searchFields.countries.end());
}
itemChanged((Wt::WCheckBox*)*countryDropDownContainer->children().begin(), countryDropDownContainer, countryOpenList, &searchFields.countries);
for (auto genderWidget: gendersDropDownContainer->children()) {
auto genderCheckBox = (Wt::WCheckBox*)genderWidget;
genderCheckBox->setChecked(searchFields.gender.find(genderCheckBox->text().toUTF8()) != searchFields.gender.end());
}
itemChanged((Wt::WCheckBox*)*gendersDropDownContainer->children().begin(), gendersDropDownContainer, gendersOpenList, &searchFields.gender);
startSearch();
}
}
Wt::WLineEdit *App::setupNameSearchField(Wt::WVBoxLayout *contentLayout) {
auto userNameEdit = addSearchItemLine<Wt::WLineEdit>(contentLayout, "Username includes");
userNameEdit->changed().connect([=, this] { searchFields.userName = userNameEdit->text().trim(); });
return userNameEdit;
}
Wt::WContainerWidget *App::setupSearchButton(Wt::WVBoxLayout *contentLayout) {
auto searchButton = addSearchItemLine<Wt::WPushButton>(contentLayout, "");
searchButton->setText("Search");
auto searchResultContainer = contentLayout->addWidget(std::make_unique<Wt::WContainerWidget>(), 1);
searchResultContainer->addNew<Wt::WText>("No results");
searchButton->clicked().connect(this, &App::startSearch);
return searchResultContainer;
}
void App::startSearch() {
if (searchFields.minAge > searchFields.maxAge) {
searchFields.outputContainer->clear();
searchFields.outputContainer->addNew<Wt::WText>("Minimum age must be at least as large as or greater than the maximum age.");
return;
}
server_.userSearch(sessionId(), searchFields.userName.toUTF8(), searchFields.minAge, searchFields.maxAge,
searchFields.countries, gendersListToShortGendersList(searchFields.gender), userName);
}
void App::showSearch(Wt::Json::Object broadcast) {
searchFields.outputContainer->clear();
auto searchResult = (Wt::Json::Array)broadcast["data"];
if (searchResult.size() == 0) {
searchFields.outputContainer->addNew<Wt::WText>("No results.");
}
auto searchListContainer = searchFields.outputContainer->addNew<Wt::WContainerWidget>();
auto searchList = searchListContainer->setLayout(std::make_unique<Wt::WVBoxLayout>());
searchListContainer->setOverflow(Wt::Overflow::Auto);
for (const Wt::Json::Object &searchItem: searchResult) {
addUserItemToLayout(searchList, searchItem);
}
triggerUpdate();
}
void App::openInbox() {
@@ -810,6 +1014,8 @@ void App::incomingBroadcast() {
showSystemMessage(broadcast);
} else if (broadcast["type"] == "conversation-start") {
showConversation(broadcast);
} else if (broadcast["type"] == "search-result") {
showSearch(broadcast);
}
}
}
@@ -912,3 +1118,24 @@ bool App::isNickAllowed(const std::string& nick) {
return lowercaseNick.find(lowercasePhrase) == std::string::npos;
});
}
template<class Class>
Class *App::addSearchItemLine(Wt::WVBoxLayout *layout, std::string label, std::unique_ptr<Wt::WContainerWidget> additionalItem) {
auto lineContainer = layout->addNew<Wt::WContainerWidget>();
lineContainer->setStyleClass("search-line");
lineContainer->setPositionScheme(Wt::PositionScheme::Relative);
auto lineLayout = lineContainer->setLayout(std::make_unique<Wt::WHBoxLayout>());
lineContainer->setPadding(Wt::WLength("0"));
lineContainer->setMargin(Wt::WLength("0"));
lineLayout->addNew<Wt::WText>(label);
lineLayout->setContentsMargins(0, 0, 0, 0);
lineLayout->setSpacing(0);
auto input = std::make_unique<Class>();
auto returnInput = input.get();
lineLayout->addWidget(std::move(input), 1);
if (additionalItem) {
lineLayout->addWidget(std::move(additionalItem));
lineContainer->setOverflow(Wt::Overflow::Visible);
}
return returnInput;
}