Search added, but change window and back lets app crash
This commit is contained in:
253
src/app.cpp
253
src/app.cpp
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user